diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/Properties.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/Properties.kt index 531955eed36..25d815bc659 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/Properties.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/Properties.kt @@ -38,6 +38,7 @@ enum class CompilerSystemProperties(val property: String, val alwaysDirectAccess COMPILE_INCREMENTAL_WITH_CLASSPATH_SNAPSHOTS("kotlin.incremental.classpath.snapshot.enabled"), COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM("kotlin.incremental.useClasspathSnapshot"), KOTLIN_COLORS_ENABLED_PROPERTY("kotlin.colors.enabled"), + LANGUAGE_VERSION_SETTINGS("kotlin.language.settings"), OS_NAME("os.name", alwaysDirectAccess = true), TMP_DIR("java.io.tmpdir"), diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/parseCommandLineArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/parseCommandLineArguments.kt index fc0aa6136c9..43132676e6b 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/parseCommandLineArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/parseCommandLineArguments.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.cli.common.arguments +import org.jetbrains.kotlin.cli.common.CompilerSystemProperties import org.jetbrains.kotlin.utils.SmartList import kotlin.reflect.KClass import kotlin.reflect.KMutableProperty1 @@ -63,13 +64,24 @@ data class ArgumentParseErrors( ) // Parses arguments into the passed [result] object. Errors related to the parsing will be collected into [CommonToolArguments.errors]. -fun parseCommandLineArguments(args: List, result: A) { +fun parseCommandLineArguments(args: List, result: A, overrideArguments: Boolean = false) { val errors = result.errors ?: ArgumentParseErrors().also { result.errors = it } val preprocessed = preprocessCommandLineArguments(args, errors) - parsePreprocessedCommandLineArguments(preprocessed, result, errors) + parsePreprocessedCommandLineArguments(preprocessed, result, errors, overrideArguments) } -private fun parsePreprocessedCommandLineArguments(args: List, result: A, errors: ArgumentParseErrors) { +fun parseCommandLineArgumentsFromEnvironment(arguments: A) { + val settingsFromEnvironment = CompilerSystemProperties.LANGUAGE_VERSION_SETTINGS.value?.takeIf { it.isNotEmpty() } + ?.split(Regex("""\s""")) ?: return + parseCommandLineArguments(settingsFromEnvironment, arguments, overrideArguments = true) +} + +private fun parsePreprocessedCommandLineArguments( + args: List, + result: A, + errors: ArgumentParseErrors, + overrideArguments: Boolean +) { data class ArgumentField(val property: KMutableProperty1, val argument: Argument) @Suppress("UNCHECKED_CAST") @@ -183,14 +195,36 @@ private fun parsePreprocessedCommandLineArguments(args errors.duplicateArguments[argument.value] = value } - updateField(property, result, value, argument.delimiter) + updateField(property, result, value, argument.delimiter, overrideArguments) } result.freeArgs += freeArgs - result.internalArguments += internalArguments + result.updateInternalArguments(internalArguments, overrideArguments) } -private fun updateField(property: KMutableProperty1, result: A, value: Any, delimiter: String) { +private fun A.updateInternalArguments( + newInternalArguments: ArrayList, + overrideArguments: Boolean +) { + val filteredExistingArguments = if (overrideArguments) { + internalArguments.filter { existingArgument -> + existingArgument !is ManualLanguageFeatureSetting || + newInternalArguments.none { + it is ManualLanguageFeatureSetting && it.languageFeature == existingArgument.languageFeature + } + } + } else internalArguments + + internalArguments = filteredExistingArguments + newInternalArguments +} + +private fun updateField( + property: KMutableProperty1, + result: A, + value: Any, + delimiter: String, + overrideArguments: Boolean +) { when (property.returnType.classifier) { Boolean::class, String::class -> property.set(result, value) Array::class -> { @@ -201,7 +235,7 @@ private fun updateField(property: KMutableProperty1? - property.set(result, if (oldValue != null) arrayOf(*oldValue, *newElements) else newElements) + property.set(result, if (oldValue != null && !overrideArguments) arrayOf(*oldValue, *newElements) else newElements) } else -> throw IllegalStateException("Unsupported argument type: ${property.returnType}") } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLITool.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLITool.kt index 2a861b4f120..aa41ca0d447 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLITool.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLITool.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.cli.common import org.fusesource.jansi.AnsiConsole import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments +import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArgumentsFromEnvironment import org.jetbrains.kotlin.cli.common.arguments.validateArguments import org.jetbrains.kotlin.cli.common.messages.* import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.INFO @@ -47,6 +48,7 @@ abstract class CLITool { ): ExitCode { val arguments = createArguments() parseCommandLineArguments(args.asList(), arguments) + parseCommandLineArgumentsFromEnvironment(arguments) val collector = PrintingMessageCollector(errStream, messageRenderer, arguments.verbose) try { diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.args b/compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.args new file mode 100644 index 00000000000..6e8e20dbc55 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.args @@ -0,0 +1,4 @@ +$TESTDATA_DIR$/appendingArgs.kt +-d +$TEMP_DIR$ +-Xallow-kotlin-package \ No newline at end of file diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.env b/compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.env new file mode 100644 index 00000000000..c8beb572999 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.env @@ -0,0 +1 @@ +-Xrender-internal-diagnostic-names \ No newline at end of file diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.kt b/compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.kt new file mode 100644 index 00000000000..f6e91613180 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.kt @@ -0,0 +1,5 @@ +package kotlin + +fun main() { + val x = 1?.dec() +} diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.out b/compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.out new file mode 100644 index 00000000000..65dcd99c86f --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.out @@ -0,0 +1,10 @@ +compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.kt:4:9: warning: [UNUSED_VARIABLE] Variable 'x' is never used + val x = 1?.dec() + ^ +compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.kt:4:13: warning: [SAFE_CALL_WILL_CHANGE_NULLABILITY] Safe call on a non-null receiver will have nullable type in future releases + val x = 1?.dec() + ^ +compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.kt:4:14: warning: [UNNECESSARY_SAFE_CALL] Unnecessary safe call on a non-null receiver of type Int + val x = 1?.dec() + ^ +OK diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/java/Annotated.java b/compiler/testData/cli/jvm/readingConfigFromEnvironment/java/Annotated.java new file mode 100644 index 00000000000..3acb8a388ac --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/java/Annotated.java @@ -0,0 +1,4 @@ +public class Annotated { + @org.jetbrains.annotations.Nullable + public static String bar() { return ""; } +} diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingArgs.args b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingArgs.args new file mode 100644 index 00000000000..19399754e4c --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingArgs.args @@ -0,0 +1,6 @@ +$TESTDATA_DIR$/overridingArgs.kt +$TESTDATA_DIR$/java +$FOREIGN_ANNOTATIONS_DIR$ +-d +$TEMP_DIR$ +-Xnullability-annotations=@org.jetbrains.annotations\:warn \ No newline at end of file diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingArgs.env b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingArgs.env new file mode 100644 index 00000000000..a029c5f14d2 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingArgs.env @@ -0,0 +1 @@ +-Xnullability-annotations=@org.jetbrains.annotations:ignore \ No newline at end of file diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingArgs.kt b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingArgs.kt new file mode 100644 index 00000000000..5c663800855 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingArgs.kt @@ -0,0 +1,3 @@ +fun main() { + Annotated.bar().length +} \ No newline at end of file diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingArgs.out b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingArgs.out new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingArgs.out @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingLv.args b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingLv.args new file mode 100644 index 00000000000..445177aaf9e --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingLv.args @@ -0,0 +1,5 @@ +$TESTDATA_DIR$/overridingLv.kt +-d +$TEMP_DIR$ +-language-version +1.7 \ No newline at end of file diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingLv.env b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingLv.env new file mode 100644 index 00000000000..a85ad999506 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingLv.env @@ -0,0 +1,2 @@ +-language-version +1.1 \ No newline at end of file diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingLv.kt b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingLv.kt new file mode 100644 index 00000000000..f1eafa99a88 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingLv.kt @@ -0,0 +1,3 @@ +fun main() { + +} \ No newline at end of file diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingLv.out b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingLv.out new file mode 100644 index 00000000000..58a0033b730 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingLv.out @@ -0,0 +1,2 @@ +error: language version 1.1 is no longer supported; please, use version 1.3 or greater. +COMPILATION_ERROR diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.args b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.args new file mode 100644 index 00000000000..81c45ff75f0 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.args @@ -0,0 +1,4 @@ +$TESTDATA_DIR$/overridingXx.kt +-d +$TEMP_DIR$ +-XXLanguage\:+ApproximateIntegerLiteralTypesInReceiverPosition \ No newline at end of file diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.env b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.env new file mode 100644 index 00000000000..ea13a2dc606 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.env @@ -0,0 +1 @@ +-XXLanguage:-ApproximateIntegerLiteralTypesInReceiverPosition \ No newline at end of file diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.kt b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.kt new file mode 100644 index 00000000000..798b83f32e7 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.kt @@ -0,0 +1,3 @@ +fun foo(ttlMillis: Long = 5 * 60 * 1000) {} + +const val cacheSize: Long = 4096 * 4 diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.out b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.out new file mode 100644 index 00000000000..4028521bde0 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.out @@ -0,0 +1,13 @@ +warning: ATTENTION! +This build uses unsafe internal compiler arguments: + +-XXLanguage:-ApproximateIntegerLiteralTypesInReceiverPosition + +This mode is not recommended for production use, +as no stability/compatibility guarantees are given on +compiler or generated code. Use it at your own risk! + +compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.kt:1:9: warning: parameter 'ttlMillis' is never used +fun foo(ttlMillis: Long = 5 * 60 * 1000) {} + ^ +OK diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.args b/compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.args new file mode 100644 index 00000000000..8b185ce8136 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.args @@ -0,0 +1,3 @@ +$TESTDATA_DIR$/simple.kt +-d +$TEMP_DIR$ diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.env b/compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.env new file mode 100644 index 00000000000..c8beb572999 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.env @@ -0,0 +1 @@ +-Xrender-internal-diagnostic-names \ No newline at end of file diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.kt b/compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.kt new file mode 100644 index 00000000000..2be36dfe0a8 --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.kt @@ -0,0 +1,3 @@ +fun main() { + val x = 1 + "" +} diff --git a/compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.out b/compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.out new file mode 100644 index 00000000000..6689ccfe65b --- /dev/null +++ b/compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.out @@ -0,0 +1,10 @@ +compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.kt:2:15: error: [NONE_APPLICABLE] None of the following functions can be called with the arguments supplied: +public final operator fun plus(other: Byte): Int defined in kotlin.Int +public final operator fun plus(other: Double): Double defined in kotlin.Int +public final operator fun plus(other: Float): Float defined in kotlin.Int +public final operator fun plus(other: Int): Int defined in kotlin.Int +public final operator fun plus(other: Long): Long defined in kotlin.Int +public final operator fun plus(other: Short): Int defined in kotlin.Int + val x = 1 + "" + ^ +COMPILATION_ERROR diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/cli/AbstractCliTest.java b/compiler/tests-common/tests/org/jetbrains/kotlin/cli/AbstractCliTest.java index 4c211c4417b..944bbfc9091 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/cli/AbstractCliTest.java +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/cli/AbstractCliTest.java @@ -27,6 +27,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.checkers.ThirdPartyAnnotationPathsKt; import org.jetbrains.kotlin.cli.common.CLITool; +import org.jetbrains.kotlin.cli.common.CompilerSystemProperties; import org.jetbrains.kotlin.cli.common.ExitCode; import org.jetbrains.kotlin.cli.common.Usage; import org.jetbrains.kotlin.cli.js.K2JSCompiler; @@ -102,6 +103,12 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir { private void doTest(@NotNull String fileName, @NotNull CLITool compiler) { System.setProperty("java.awt.headless", "true"); + + File environmentTestConfig = new File(fileName.replaceFirst("\\.args$", ".env")); + if (environmentTestConfig.exists()) { + CompilerSystemProperties.LANGUAGE_VERSION_SETTINGS.setValue(FilesKt.readText(environmentTestConfig, Charsets.UTF_8)); + } + Pair outputAndExitCode = executeCompilerGrabOutput(compiler, readArgs(fileName, tmpdir.getPath())); String actual = getNormalizedCompilerOutput( outputAndExitCode.getFirst(), outputAndExitCode.getSecond(), new File(fileName).getParent() diff --git a/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt b/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt index 44ef6addce8..c75b7e3b470 100644 --- a/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt +++ b/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt @@ -250,6 +250,7 @@ fun generateJUnit3CompilerTests(args: Array) { } testClass { + model("cli/jvm/readingConfigFromEnvironment", extension = "args", testMethod = "doJvmTest", recursive = false) model("cli/jvm", extension = "args", testMethod = "doJvmTest", recursive = false) model("cli/js", extension = "args", testMethod = "doJsTest", recursive = false) model("cli/js-dce", extension = "args", testMethod = "doJsDceTest", recursive = false) diff --git a/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java index cc45d190461..28499178ba7 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java @@ -19,6 +19,44 @@ import java.util.regex.Pattern; @SuppressWarnings("all") @RunWith(JUnit3RunnerWithInners.class) public class CliTestGenerated extends AbstractCliTest { + @TestMetadata("compiler/testData/cli/jvm/readingConfigFromEnvironment") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ReadingConfigFromEnvironment extends AbstractCliTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doJvmTest, this, testDataFilePath); + } + + public void testAllFilesPresentInReadingConfigFromEnvironment() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/cli/jvm/readingConfigFromEnvironment"), Pattern.compile("^(.+)\\.args$"), null, false); + } + + @TestMetadata("appendingArgs.args") + public void testAppendingArgs() throws Exception { + runTest("compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.args"); + } + + @TestMetadata("overridingArgs.args") + public void testOverridingArgs() throws Exception { + runTest("compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingArgs.args"); + } + + @TestMetadata("overridingLv.args") + public void testOverridingLv() throws Exception { + runTest("compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingLv.args"); + } + + @TestMetadata("overridingXx.args") + public void testOverridingXx() throws Exception { + runTest("compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.args"); + } + + @TestMetadata("simple.args") + public void testSimple() throws Exception { + runTest("compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.args"); + } + } + @TestMetadata("compiler/testData/cli/jvm") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)