Add support of internal arguments for language feature settings
Arguments are passed in form '-XXLanguage:+LanguageFeatureName' for enabling
LanguageFeature.LanguageFeatureName, and '-XXLanguage:-LanguageFeatureName'
for disabling.
Note that they do override other settings, including 'language-version'
or extra ('-X') args.
This commit is contained in:
+26
@@ -230,8 +230,34 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
||||
if (!contains(it)) put(it, LanguageFeature.State.ENABLED)
|
||||
}
|
||||
}
|
||||
|
||||
// Internal arguments should go last, because it may be useful to override
|
||||
// some feature state via -XX (even if some -X flags were passed)
|
||||
if (internalArguments.isNotEmpty()) {
|
||||
configureLanguageFeaturesFromInternalArgs(collector)
|
||||
}
|
||||
}
|
||||
|
||||
private fun HashMap<LanguageFeature, LanguageFeature.State>.configureLanguageFeaturesFromInternalArgs(collector: MessageCollector) {
|
||||
val languageSettingsParser = LanguageSettingsParser()
|
||||
val featuresThatForcePreReleaseBinaries = mutableListOf<LanguageFeature>()
|
||||
|
||||
for (argument in internalArguments) {
|
||||
val (feature, state) = languageSettingsParser.parseInternalArgument(argument, collector) ?: continue
|
||||
put(feature, state)
|
||||
if (state == LanguageFeature.State.ENABLED && feature.forcesPreReleaseBinariesIfEnabled()) {
|
||||
featuresThatForcePreReleaseBinaries += feature
|
||||
}
|
||||
}
|
||||
|
||||
if (featuresThatForcePreReleaseBinaries.isNotEmpty()) {
|
||||
collector.report(
|
||||
CompilerMessageSeverity.STRONG_WARNING,
|
||||
"Following manually enabled features will force generation of pre-release binaries: ${featuresThatForcePreReleaseBinaries.joinToString()}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun configureLanguageVersionSettings(collector: MessageCollector): LanguageVersionSettings {
|
||||
|
||||
// If only "-api-version" is specified, language version is assumed to be the latest stable
|
||||
|
||||
+2
@@ -49,4 +49,6 @@ abstract class CommonToolArguments : Freezable(), Serializable {
|
||||
@GradleOption(DefaultValues.BooleanFalseDefault::class)
|
||||
@Argument(value = "-Werror", description = "Report an error if there are any warnings")
|
||||
var allWarningsAsErrors: Boolean by FreezableVar(false)
|
||||
|
||||
var internalArguments: List<String> by FreezableVar(emptyList())
|
||||
}
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.common.arguments
|
||||
|
||||
import org.jetbrains.kotlin.cli.common.arguments.InternalArgumentParser.Companion.INTERNAL_ARGUMENT_PREFIX
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
|
||||
/**
|
||||
* Arguments that can drastically change compiler behavior,
|
||||
* breaking stability/compatibility.
|
||||
*
|
||||
* Internal arguments are split into 'families', each family
|
||||
* with its own set of arguments, settings and parsing rules
|
||||
*
|
||||
* Internal arguments start with '-XX' prefix, followed by
|
||||
* family name. Everything after that is handled by the corresponding
|
||||
* parser of that particular family.
|
||||
*/
|
||||
interface InternalArgumentParser<A : InternalArgument> {
|
||||
// Should be fast
|
||||
fun canParse(arg: String): Boolean
|
||||
|
||||
fun parseInternalArgument(arg: String, messageCollector: MessageCollector): A?
|
||||
|
||||
companion object {
|
||||
internal const val INTERNAL_ARGUMENT_PREFIX = "-XX"
|
||||
|
||||
internal val PARSERS: List<InternalArgumentParser<*>> = listOf(
|
||||
LanguageSettingsParser()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractInternalArgumentParser<A : InternalArgument>(familyName: String) : InternalArgumentParser<A> {
|
||||
private val wholePrefix: String = INTERNAL_ARGUMENT_PREFIX + familyName
|
||||
|
||||
override fun canParse(arg: String): Boolean = arg.startsWith(wholePrefix)
|
||||
|
||||
override fun parseInternalArgument(arg: String, messageCollector: MessageCollector): A? {
|
||||
if (!arg.startsWith(wholePrefix)) return null
|
||||
|
||||
return parseTail(arg.removePrefix(wholePrefix), arg, messageCollector)
|
||||
}
|
||||
|
||||
abstract fun parseTail(tail: String, wholeArgument: String, messageCollector: MessageCollector): A?
|
||||
}
|
||||
|
||||
|
||||
// Arguments of form '-XXLanguage:+LanguageFeature' or '-XXLanguage:-LanguageFeature', which enable or disable corresponding LanguageFeature.
|
||||
class LanguageSettingsParser : AbstractInternalArgumentParser<ManualLanguageFeatureSetting>("Language") {
|
||||
|
||||
// Expected tail form: ':(+|-)<language feature name>'
|
||||
override fun parseTail(tail: String, wholeArgument: String, messageCollector: MessageCollector): ManualLanguageFeatureSetting? {
|
||||
fun reportAndReturnNull(message: String): Nothing? {
|
||||
messageCollector.report(CompilerMessageSeverity.STRONG_WARNING, message)
|
||||
return null
|
||||
}
|
||||
|
||||
val colon = tail.getOrNull(0) ?: return reportAndReturnNull("Incorrect internal argument syntax, missing colon: $wholeArgument")
|
||||
|
||||
val modificator = tail.getOrNull(1)
|
||||
val languageFeatureState = when (modificator) {
|
||||
'+' -> LanguageFeature.State.ENABLED
|
||||
|
||||
'-' -> LanguageFeature.State.DISABLED
|
||||
|
||||
else -> return reportAndReturnNull("Incorrect internal argument syntax, missing modificator: $wholeArgument")
|
||||
}
|
||||
|
||||
val languageFeatureName = tail.substring(2)
|
||||
if (languageFeatureName.isEmpty()) return reportAndReturnNull("Empty language feature name for internal argument '$wholeArgument'")
|
||||
|
||||
val languageFeature = LanguageFeature.fromString(languageFeatureName)
|
||||
?: return reportAndReturnNull("Unknown language feature '$languageFeatureName' in passed internal argument '$wholeArgument'")
|
||||
|
||||
return ManualLanguageFeatureSetting(languageFeature, languageFeatureState)
|
||||
}
|
||||
}
|
||||
|
||||
interface InternalArgument
|
||||
|
||||
data class ManualLanguageFeatureSetting(val languageFeature: LanguageFeature, val state: LanguageFeature.State) : InternalArgument
|
||||
+17
-1
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.cli.common.arguments
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KMutableProperty1
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.memberProperties
|
||||
|
||||
@Target(AnnotationTarget.PROPERTY)
|
||||
@@ -102,6 +101,7 @@ fun <A : CommonToolArguments> parseCommandLineArguments(args: List<String>, resu
|
||||
}
|
||||
|
||||
val freeArgs = ArrayList<String>()
|
||||
val internalArguments = ArrayList<String>()
|
||||
|
||||
var i = 0
|
||||
loop@ while (i < args.size) {
|
||||
@@ -116,6 +116,21 @@ fun <A : CommonToolArguments> parseCommandLineArguments(args: List<String>, resu
|
||||
continue
|
||||
}
|
||||
|
||||
if (arg.startsWith(InternalArgumentParser.INTERNAL_ARGUMENT_PREFIX)) {
|
||||
val matchingParsers = InternalArgumentParser.PARSERS.filter { it.canParse(arg) }
|
||||
assert(matchingParsers.size <= 1) { "Internal error: internal argument $arg can be ambiguously parsed by parsers ${matchingParsers.joinToString()}" }
|
||||
|
||||
val parser = matchingParsers.firstOrNull()
|
||||
|
||||
if (parser == null) {
|
||||
errors.unknownExtraFlags += arg
|
||||
} else {
|
||||
internalArguments.add(arg)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
val argumentField = properties.firstOrNull { it.matches(arg) }
|
||||
if (argumentField == null) {
|
||||
when {
|
||||
@@ -154,6 +169,7 @@ fun <A : CommonToolArguments> parseCommandLineArguments(args: List<String>, resu
|
||||
}
|
||||
|
||||
result.freeArgs += freeArgs
|
||||
result.internalArguments += internalArguments
|
||||
}
|
||||
|
||||
private fun <A : CommonToolArguments> updateField(property: KMutableProperty1<A, Any?>, result: A, value: Any, delimiter: String) {
|
||||
|
||||
Reference in New Issue
Block a user