Introduce LanguageFeature.State, drop coroutines-related pseudofeatures
Previously there were three LanguageFeature instances -- Coroutines, DoNotWarnOnCoroutines and ErrorOnCoroutines -- which were handled very awkwardly in the compiler and in the IDE to basically support a language feature with a more complex state: not just enabled/disabled, but also enabled with warning and enabled with error. Introduce a new enum LanguageFeature.State for this and allow LanguageVersionSettings to get the state of any language feature with 'getFeatureSupport'. One noticeable drawback of this approach is that looking at the API, one may assume that any language feature can be in one of the four states (enabled, warning, error, disabled). This is not true however; there's only one language feature at the moment (coroutines) for which these intermediate states (warning, error) are handled in any way. This may be refactored further by abstracting the logic that checks the language feature availability so that it would work exactly the same for any feature. Another issue is that the difference among ENABLED_WITH_ERROR and DISABLED is not clear. They are left as separate states because at the moment, different diagnostics are reported in these two cases and quick-fixes in IDE rely on that
This commit is contained in:
@@ -40,8 +40,9 @@ import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStat
|
|||||||
import org.jetbrains.kotlin.utils.StringsKt;
|
import org.jetbrains.kotlin.utils.StringsKt;
|
||||||
|
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.util.ArrayList;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.cli.common.ExitCode.*;
|
import static org.jetbrains.kotlin.cli.common.ExitCode.*;
|
||||||
@@ -261,17 +262,17 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<LanguageFeature> extraLanguageFeatures = new ArrayList<LanguageFeature>(0);
|
Map<LanguageFeature, LanguageFeature.State> extraLanguageFeatures = new HashMap<LanguageFeature, LanguageFeature.State>(0);
|
||||||
if (arguments.multiPlatform) {
|
if (arguments.multiPlatform) {
|
||||||
extraLanguageFeatures.add(LanguageFeature.MultiPlatformProjects);
|
extraLanguageFeatures.put(LanguageFeature.MultiPlatformProjects, LanguageFeature.State.ENABLED);
|
||||||
}
|
}
|
||||||
if (arguments.noCheckImpl) {
|
if (arguments.noCheckImpl) {
|
||||||
extraLanguageFeatures.add(LanguageFeature.MultiPlatformDoNotCheckImpl);
|
extraLanguageFeatures.put(LanguageFeature.MultiPlatformDoNotCheckImpl, LanguageFeature.State.ENABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
LanguageFeature coroutinesApplicabilityLevel = chooseCoroutinesApplicabilityLevel(configuration, arguments);
|
LanguageFeature.State coroutinesState = chooseCoroutinesApplicabilityLevel(configuration, arguments);
|
||||||
if (coroutinesApplicabilityLevel != null) {
|
if (coroutinesState != null) {
|
||||||
extraLanguageFeatures.add(coroutinesApplicabilityLevel);
|
extraLanguageFeatures.put(LanguageFeature.Coroutines, coroutinesState);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommonConfigurationKeysKt.setLanguageVersionSettings(
|
CommonConfigurationKeysKt.setLanguageVersionSettings(
|
||||||
@@ -287,15 +288,15 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static LanguageFeature chooseCoroutinesApplicabilityLevel(
|
private static LanguageFeature.State chooseCoroutinesApplicabilityLevel(
|
||||||
@NotNull CompilerConfiguration configuration,
|
@NotNull CompilerConfiguration configuration,
|
||||||
@NotNull CommonCompilerArguments arguments
|
@NotNull CommonCompilerArguments arguments
|
||||||
) {
|
) {
|
||||||
if (arguments.coroutinesError && !arguments.coroutinesWarn && !arguments.coroutinesEnable) {
|
if (arguments.coroutinesError && !arguments.coroutinesWarn && !arguments.coroutinesEnable) {
|
||||||
return LanguageFeature.ErrorOnCoroutines;
|
return LanguageFeature.State.ENABLED_WITH_ERROR;
|
||||||
}
|
}
|
||||||
else if (arguments.coroutinesEnable && !arguments.coroutinesWarn && !arguments.coroutinesError) {
|
else if (arguments.coroutinesEnable && !arguments.coroutinesWarn && !arguments.coroutinesError) {
|
||||||
return LanguageFeature.DoNotWarnOnCoroutines;
|
return LanguageFeature.State.ENABLED;
|
||||||
}
|
}
|
||||||
else if (!arguments.coroutinesEnable && !arguments.coroutinesError) {
|
else if (!arguments.coroutinesEnable && !arguments.coroutinesError) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+5
-9
@@ -123,17 +123,13 @@ object JvmRuntimeVersionsConsistencyChecker {
|
|||||||
if (@Suppress("DEPRECATION") languageVersionSettings.isApiVersionExplicit)
|
if (@Suppress("DEPRECATION") languageVersionSettings.isApiVersionExplicit)
|
||||||
languageVersionSettings.apiVersion
|
languageVersionSettings.apiVersion
|
||||||
else
|
else
|
||||||
|
// "minOf" is needed in case when API version was inferred from language version and it's older than actualApi.
|
||||||
|
// For example, in "kotlinc-1.2 -language-version 1.0 -cp kotlin-runtime-1.1.jar" we should still infer API = 1.0
|
||||||
minOf(languageVersionSettings.apiVersion, actualApi)
|
minOf(languageVersionSettings.apiVersion, actualApi)
|
||||||
|
|
||||||
// "minOf" is needed in case when API version was inferred from language version and it's older than actualApi.
|
val newSettings = object : LanguageVersionSettings by languageVersionSettings {
|
||||||
// For example, in "kotlinc-1.2 -language-version 1.0 -cp kotlin-runtime-1.1.jar" we should still infer API = 1.0
|
override val apiVersion: ApiVersion get() = inferredApiVersion
|
||||||
val newSettings = LanguageVersionSettingsImpl(
|
}
|
||||||
languageVersionSettings.languageVersion,
|
|
||||||
inferredApiVersion,
|
|
||||||
languageVersionSettings.skipMetadataVersionCheck,
|
|
||||||
languageVersionSettings.additionalFeatures,
|
|
||||||
isApiVersionExplicit = false
|
|
||||||
)
|
|
||||||
|
|
||||||
messageCollector.issue(null, "Old runtime has been found in the classpath. " +
|
messageCollector.issue(null, "Old runtime has been found in the classpath. " +
|
||||||
"Initial language version settings: $languageVersionSettings. " +
|
"Initial language version settings: $languageVersionSettings. " +
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragmen
|
|||||||
*/
|
*/
|
||||||
object DefaultAnalyzerFacade : AnalyzerFacade<PlatformAnalysisParameters>() {
|
object DefaultAnalyzerFacade : AnalyzerFacade<PlatformAnalysisParameters>() {
|
||||||
private val languageVersionSettings = LanguageVersionSettingsImpl(
|
private val languageVersionSettings = LanguageVersionSettingsImpl(
|
||||||
LanguageVersion.LATEST, ApiVersion.LATEST, additionalFeatures = setOf(LanguageFeature.MultiPlatformProjects)
|
LanguageVersion.LATEST, ApiVersion.LATEST,
|
||||||
|
specificFeatures = mapOf(LanguageFeature.MultiPlatformProjects to LanguageFeature.State.ENABLED)
|
||||||
)
|
)
|
||||||
|
|
||||||
private class SourceModuleInfo(
|
private class SourceModuleInfo(
|
||||||
|
|||||||
@@ -99,14 +99,6 @@ object ModifierCheckerCore {
|
|||||||
IMPL_KEYWORD to LanguageFeature.MultiPlatformProjects
|
IMPL_KEYWORD to LanguageFeature.MultiPlatformProjects
|
||||||
)
|
)
|
||||||
|
|
||||||
val errorOnFeature = mapOf(
|
|
||||||
LanguageFeature.Coroutines to LanguageFeature.ErrorOnCoroutines
|
|
||||||
)
|
|
||||||
|
|
||||||
val noWarningOnFeature = mapOf(
|
|
||||||
LanguageFeature.Coroutines to LanguageFeature.DoNotWarnOnCoroutines
|
|
||||||
)
|
|
||||||
|
|
||||||
val featureDependenciesTargets = mapOf(
|
val featureDependenciesTargets = mapOf(
|
||||||
LanguageFeature.InlineProperties to setOf(PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER)
|
LanguageFeature.InlineProperties to setOf(PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER)
|
||||||
)
|
)
|
||||||
@@ -280,27 +272,25 @@ object ModifierCheckerCore {
|
|||||||
|
|
||||||
val dependency = featureDependencies[modifier] ?: return true
|
val dependency = featureDependencies[modifier] ?: return true
|
||||||
|
|
||||||
val errorOnDependencyFeature = errorOnFeature[dependency]?.let { languageVersionSettings.supportsFeature(it) } ?: false
|
val featureSupport = languageVersionSettings.getFeatureSupport(dependency)
|
||||||
val supportsFeature = languageVersionSettings.supportsFeature(dependency)
|
|
||||||
|
|
||||||
val diagnosticData = dependency to languageVersionSettings
|
val diagnosticData = dependency to languageVersionSettings
|
||||||
if (!supportsFeature || errorOnDependencyFeature) {
|
if (featureSupport == LanguageFeature.State.ENABLED_WITH_ERROR || featureSupport == LanguageFeature.State.DISABLED) {
|
||||||
val restrictedTargets = featureDependenciesTargets[dependency]
|
val restrictedTargets = featureDependenciesTargets[dependency]
|
||||||
if (restrictedTargets != null && actualTargets.intersect(restrictedTargets).isEmpty()) {
|
if (restrictedTargets != null && actualTargets.intersect(restrictedTargets).isEmpty()) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!supportsFeature) {
|
if (featureSupport == LanguageFeature.State.DISABLED) {
|
||||||
trace.report(Errors.UNSUPPORTED_FEATURE.on(node.psi, diagnosticData))
|
trace.report(Errors.UNSUPPORTED_FEATURE.on(node.psi, diagnosticData))
|
||||||
}
|
}
|
||||||
else if (errorOnDependencyFeature) {
|
else {
|
||||||
trace.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(node.psi, diagnosticData))
|
trace.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(node.psi, diagnosticData))
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val pairedNoWarningFeature = noWarningOnFeature[dependency]
|
if (featureSupport == LanguageFeature.State.ENABLED_WITH_WARNING) {
|
||||||
if (pairedNoWarningFeature != null && !languageVersionSettings.supportsFeature(pairedNoWarningFeature)) {
|
|
||||||
trace.report(Errors.EXPERIMENTAL_FEATURE_WARNING.on(node.psi, diagnosticData))
|
trace.report(Errors.EXPERIMENTAL_FEATURE_WARNING.on(node.psi, diagnosticData))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-8
@@ -90,14 +90,18 @@ object BuilderFunctionsCallChecker : CallChecker {
|
|||||||
|
|
||||||
fun checkCoroutinesFeature(languageVersionSettings: LanguageVersionSettings, diagnosticHolder: DiagnosticSink, reportOn: PsiElement) {
|
fun checkCoroutinesFeature(languageVersionSettings: LanguageVersionSettings, diagnosticHolder: DiagnosticSink, reportOn: PsiElement) {
|
||||||
val diagnosticData = LanguageFeature.Coroutines to languageVersionSettings
|
val diagnosticData = LanguageFeature.Coroutines to languageVersionSettings
|
||||||
if (!languageVersionSettings.supportsFeature(LanguageFeature.Coroutines)) {
|
when (languageVersionSettings.getFeatureSupport(LanguageFeature.Coroutines)) {
|
||||||
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(reportOn, diagnosticData))
|
LanguageFeature.State.ENABLED -> {
|
||||||
}
|
}
|
||||||
else if (languageVersionSettings.supportsFeature(LanguageFeature.ErrorOnCoroutines)) {
|
LanguageFeature.State.ENABLED_WITH_WARNING -> {
|
||||||
diagnosticHolder.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(reportOn, diagnosticData))
|
diagnosticHolder.report(Errors.EXPERIMENTAL_FEATURE_WARNING.on(reportOn, diagnosticData))
|
||||||
}
|
}
|
||||||
else if (!languageVersionSettings.supportsFeature(LanguageFeature.DoNotWarnOnCoroutines)) {
|
LanguageFeature.State.ENABLED_WITH_ERROR -> {
|
||||||
diagnosticHolder.report(Errors.EXPERIMENTAL_FEATURE_WARNING.on(reportOn, diagnosticData))
|
diagnosticHolder.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(reportOn, diagnosticData))
|
||||||
|
}
|
||||||
|
LanguageFeature.State.DISABLED -> {
|
||||||
|
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(reportOn, diagnosticData))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -76,7 +76,8 @@ The directive lets you compose a test consisting of several files in one actual
|
|||||||
|
|
||||||
### 4. LANGUAGE
|
### 4. LANGUAGE
|
||||||
|
|
||||||
This directive lets you enable or disable certain language features. Language features are named as enum entries of the class `LanguageFeature`. Each feature can be enabled with `+` or disabled with `-`.
|
This directive lets you enable or disable certain language features. Language features are named as enum entries of the class `LanguageFeature`.
|
||||||
|
Each feature can be enabled with `+`, disabled with `-`, or enabled with warning with `warn:`.
|
||||||
|
|
||||||
#### Usage:
|
#### Usage:
|
||||||
|
|
||||||
@@ -84,6 +85,8 @@ This directive lets you enable or disable certain language features. Language fe
|
|||||||
|
|
||||||
// !LANGUAGE: +TypeAliases -LocalDelegatedProperties
|
// !LANGUAGE: +TypeAliases -LocalDelegatedProperties
|
||||||
|
|
||||||
|
// !LANGUAGE: warn:Coroutines
|
||||||
|
|
||||||
### 5. API_VERSION
|
### 5. API_VERSION
|
||||||
|
|
||||||
This directive emulates the behavior of the `-api-version` command line option, disallowing to use declarations annotated with `@SinceKotlin(X)` where X is greater than the specified API version.
|
This directive emulates the behavior of the `-api-version` command line option, disallowing to use declarations annotated with `@SinceKotlin(X)` where X is greater than the specified API version.
|
||||||
|
|||||||
+4
-4
@@ -1,16 +1,16 @@
|
|||||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
// !LANGUAGE: +ErrorOnCoroutines
|
// !LANGUAGE: -Coroutines
|
||||||
|
|
||||||
<!EXPERIMENTAL_FEATURE_ERROR!>suspend<!> fun suspendHere(): String = "OK"
|
<!UNSUPPORTED_FEATURE!>suspend<!> fun suspendHere(): String = "OK"
|
||||||
|
|
||||||
fun builder(c: <!EXPERIMENTAL_FEATURE_ERROR!>suspend<!> () -> Unit) {
|
fun builder(c: <!UNSUPPORTED_FEATURE!>suspend<!> () -> Unit) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
var result = ""
|
var result = ""
|
||||||
|
|
||||||
<!EXPERIMENTAL_FEATURE_ERROR!>builder<!> {
|
<!UNSUPPORTED_FEATURE!>builder<!> {
|
||||||
suspendHere()
|
suspendHere()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
// !LANGUAGE: -DoNotWarnOnCoroutines
|
// !LANGUAGE: warn:Coroutines
|
||||||
|
|
||||||
<!EXPERIMENTAL_FEATURE_WARNING!>suspend<!> fun suspendHere(): String = "OK"
|
<!EXPERIMENTAL_FEATURE_WARNING!>suspend<!> fun suspendHere(): String = "OK"
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// !API_VERSION: 1.1
|
// !API_VERSION: 1.1
|
||||||
// !LANGUAGE: +DoNotWarnOnCoroutines
|
// !LANGUAGE: +Coroutines
|
||||||
// SKIP_TXT
|
// SKIP_TXT
|
||||||
|
|
||||||
import kotlin.coroutines.experimental.*
|
import kotlin.coroutines.experimental.*
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result ?: BaseDiagnosticsTest.DiagnosticTestLanguageVersionSettings(
|
return result ?: BaseDiagnosticsTest.DiagnosticTestLanguageVersionSettings(
|
||||||
BaseDiagnosticsTest.DEFAULT_DIAGNOSTIC_TESTS_FEATURES.keysToMap { true },
|
BaseDiagnosticsTest.DEFAULT_DIAGNOSTIC_TESTS_FEATURES,
|
||||||
LanguageVersionSettingsImpl.DEFAULT.apiVersion,
|
LanguageVersionSettingsImpl.DEFAULT.apiVersion,
|
||||||
LanguageVersionSettingsImpl.DEFAULT.languageVersion
|
LanguageVersionSettingsImpl.DEFAULT.languageVersion
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -99,20 +99,17 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class DiagnosticTestLanguageVersionSettings(
|
data class DiagnosticTestLanguageVersionSettings(
|
||||||
private val languageFeatures: Map<LanguageFeature, Boolean>,
|
private val languageFeatures: Map<LanguageFeature, LanguageFeature.State>,
|
||||||
override val apiVersion: ApiVersion,
|
override val apiVersion: ApiVersion,
|
||||||
override val languageVersion: LanguageVersion
|
override val languageVersion: LanguageVersion
|
||||||
) : LanguageVersionSettings {
|
) : LanguageVersionSettings {
|
||||||
private val delegate = LanguageVersionSettingsImpl(languageVersion, apiVersion)
|
private val delegate = LanguageVersionSettingsImpl(languageVersion, apiVersion)
|
||||||
|
|
||||||
override fun supportsFeature(feature: LanguageFeature): Boolean =
|
override fun getFeatureSupport(feature: LanguageFeature): LanguageFeature.State =
|
||||||
languageFeatures[feature] ?: delegate.supportsFeature(feature)
|
languageFeatures[feature] ?: delegate.getFeatureSupport(feature)
|
||||||
|
|
||||||
override val skipMetadataVersionCheck: Boolean get() = false
|
override val skipMetadataVersionCheck: Boolean get() = false
|
||||||
|
|
||||||
override val additionalFeatures: Collection<LanguageFeature>
|
|
||||||
get() = error("Must not be called")
|
|
||||||
|
|
||||||
override val isApiVersionExplicit: Boolean
|
override val isApiVersionExplicit: Boolean
|
||||||
get() = error("Must not be called")
|
get() = error("Must not be called")
|
||||||
}
|
}
|
||||||
@@ -286,10 +283,10 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
|
|||||||
|
|
||||||
val LANGUAGE_DIRECTIVE = "LANGUAGE"
|
val LANGUAGE_DIRECTIVE = "LANGUAGE"
|
||||||
val LANGUAGE_VERSION = "LANGUAGE_VERSION"
|
val LANGUAGE_VERSION = "LANGUAGE_VERSION"
|
||||||
private val LANGUAGE_PATTERN = Pattern.compile("([\\+\\-])(\\w+)\\s*")
|
private val LANGUAGE_PATTERN = Pattern.compile("(\\+|\\-|warn:)(\\w+)\\s*")
|
||||||
|
|
||||||
val DEFAULT_DIAGNOSTIC_TESTS_FEATURES = listOf(
|
val DEFAULT_DIAGNOSTIC_TESTS_FEATURES = mapOf(
|
||||||
LanguageFeature.DoNotWarnOnCoroutines
|
LanguageFeature.Coroutines to LanguageFeature.State.ENABLED
|
||||||
)
|
)
|
||||||
|
|
||||||
val API_VERSION_DIRECTIVE = "API_VERSION"
|
val API_VERSION_DIRECTIVE = "API_VERSION"
|
||||||
@@ -330,27 +327,32 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
|
|||||||
return DiagnosticTestLanguageVersionSettings(languageFeatures, apiVersion, languageVersion)
|
return DiagnosticTestLanguageVersionSettings(languageFeatures, apiVersion, languageVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectLanguageFeatureMap(directives: String): Map<LanguageFeature, Boolean> {
|
private fun collectLanguageFeatureMap(directives: String): Map<LanguageFeature, LanguageFeature.State> {
|
||||||
val matcher = LANGUAGE_PATTERN.matcher(directives)
|
val matcher = LANGUAGE_PATTERN.matcher(directives)
|
||||||
if (!matcher.find()) {
|
if (!matcher.find()) {
|
||||||
Assert.fail(
|
Assert.fail(
|
||||||
"Wrong syntax in the '// !$LANGUAGE_DIRECTIVE: ...' directive:\n" +
|
"Wrong syntax in the '// !$LANGUAGE_DIRECTIVE: ...' directive:\n" +
|
||||||
"found: '$directives'\n" +
|
"found: '$directives'\n" +
|
||||||
"Must be '([+-]LanguageFeatureName)+'\n" +
|
"Must be '((+|-|warn:)LanguageFeatureName)+'\n" +
|
||||||
"where '+' means 'enable' and '-' means 'disable'\n" +
|
"where '+' means 'enable', '-' means 'disable', 'warn:' means 'enable with warning'\n" +
|
||||||
"and language feature names are names of enum entries in LanguageFeature enum class"
|
"and language feature names are names of enum entries in LanguageFeature enum class"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val values = HashMap<LanguageFeature, Boolean>()
|
val values = HashMap<LanguageFeature, LanguageFeature.State>()
|
||||||
do {
|
do {
|
||||||
val enable = matcher.group(1) == "+"
|
val mode = when (matcher.group(1)) {
|
||||||
|
"+" -> LanguageFeature.State.ENABLED
|
||||||
|
"-" -> LanguageFeature.State.DISABLED
|
||||||
|
"warn:" -> LanguageFeature.State.ENABLED_WITH_WARNING
|
||||||
|
else -> error("Unknown mode for language feature: ${matcher.group(1)}")
|
||||||
|
}
|
||||||
val name = matcher.group(2)
|
val name = matcher.group(2)
|
||||||
val feature = LanguageFeature.fromString(name) ?: throw AssertionError(
|
val feature = LanguageFeature.fromString(name) ?: throw AssertionError(
|
||||||
"Language feature not found, please check spelling: $name\n" +
|
"Language feature not found, please check spelling: $name\n" +
|
||||||
"Known features:\n ${LanguageFeature.values().joinToString("\n ")}"
|
"Known features:\n ${LanguageFeature.values().joinToString("\n ")}"
|
||||||
)
|
)
|
||||||
if (values.put(feature, enable) != null) {
|
if (values.put(feature, mode) != null) {
|
||||||
Assert.fail("Duplicate entry for the language feature: $name")
|
Assert.fail("Duplicate entry for the language feature: $name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,14 +22,14 @@ import org.jetbrains.kotlin.utils.DescriptionAware
|
|||||||
enum class LanguageFeature(
|
enum class LanguageFeature(
|
||||||
val sinceVersion: LanguageVersion?,
|
val sinceVersion: LanguageVersion?,
|
||||||
val sinceApiVersion: ApiVersion = ApiVersion.KOTLIN_1_0,
|
val sinceApiVersion: ApiVersion = ApiVersion.KOTLIN_1_0,
|
||||||
val hintUrl: String? = null
|
val hintUrl: String? = null,
|
||||||
|
val defaultState: State = State.ENABLED
|
||||||
) {
|
) {
|
||||||
// Note: names of these entries are also used in diagnostic tests and in user-visible messages (see presentableText below)
|
// Note: names of these entries are also used in diagnostic tests and in user-visible messages (see presentableText below)
|
||||||
TypeAliases(KOTLIN_1_1),
|
TypeAliases(KOTLIN_1_1),
|
||||||
BoundCallableReferences(KOTLIN_1_1, ApiVersion.KOTLIN_1_1),
|
BoundCallableReferences(KOTLIN_1_1, ApiVersion.KOTLIN_1_1),
|
||||||
LocalDelegatedProperties(KOTLIN_1_1, ApiVersion.KOTLIN_1_1),
|
LocalDelegatedProperties(KOTLIN_1_1, ApiVersion.KOTLIN_1_1),
|
||||||
TopLevelSealedInheritance(KOTLIN_1_1),
|
TopLevelSealedInheritance(KOTLIN_1_1),
|
||||||
Coroutines(KOTLIN_1_1, ApiVersion.KOTLIN_1_1, "https://kotlinlang.org/docs/diagnostics/experimental-coroutines"),
|
|
||||||
AdditionalBuiltInsMembers(KOTLIN_1_1),
|
AdditionalBuiltInsMembers(KOTLIN_1_1),
|
||||||
DataClassInheritance(KOTLIN_1_1),
|
DataClassInheritance(KOTLIN_1_1),
|
||||||
InlineProperties(KOTLIN_1_1),
|
InlineProperties(KOTLIN_1_1),
|
||||||
@@ -49,11 +49,12 @@ enum class LanguageFeature(
|
|||||||
DefaultImportOfPackageKotlinComparisons(KOTLIN_1_1),
|
DefaultImportOfPackageKotlinComparisons(KOTLIN_1_1),
|
||||||
|
|
||||||
// Experimental features
|
// Experimental features
|
||||||
MultiPlatformProjects(null),
|
|
||||||
MultiPlatformDoNotCheckImpl(null),
|
|
||||||
|
|
||||||
DoNotWarnOnCoroutines(null),
|
Coroutines(KOTLIN_1_1, ApiVersion.KOTLIN_1_1, "https://kotlinlang.org/docs/diagnostics/experimental-coroutines", State.ENABLED_WITH_WARNING),
|
||||||
ErrorOnCoroutines(null)
|
|
||||||
|
MultiPlatformProjects(null, defaultState = State.DISABLED),
|
||||||
|
MultiPlatformDoNotCheckImpl(null, defaultState = State.DISABLED),
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
val presentableName: String
|
val presentableName: String
|
||||||
@@ -62,6 +63,13 @@ enum class LanguageFeature(
|
|||||||
|
|
||||||
val presentableText get() = if (hintUrl == null) presentableName else "$presentableName (See: $hintUrl)"
|
val presentableText get() = if (hintUrl == null) presentableName else "$presentableName (See: $hintUrl)"
|
||||||
|
|
||||||
|
enum class State {
|
||||||
|
ENABLED,
|
||||||
|
ENABLED_WITH_WARNING,
|
||||||
|
ENABLED_WITH_ERROR,
|
||||||
|
DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun fromString(str: String) = values().find { it.name == str }
|
fun fromString(str: String) = values().find { it.name == str }
|
||||||
@@ -93,7 +101,10 @@ enum class LanguageVersion(val major: Int, val minor: Int) : DescriptionAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface LanguageVersionSettings {
|
interface LanguageVersionSettings {
|
||||||
fun supportsFeature(feature: LanguageFeature): Boolean
|
fun getFeatureSupport(feature: LanguageFeature): LanguageFeature.State
|
||||||
|
|
||||||
|
fun supportsFeature(feature: LanguageFeature): Boolean =
|
||||||
|
getFeatureSupport(feature).let { it == LanguageFeature.State.ENABLED || it == LanguageFeature.State.ENABLED_WITH_WARNING }
|
||||||
|
|
||||||
val apiVersion: ApiVersion
|
val apiVersion: ApiVersion
|
||||||
|
|
||||||
@@ -102,8 +113,6 @@ interface LanguageVersionSettings {
|
|||||||
|
|
||||||
val skipMetadataVersionCheck: Boolean
|
val skipMetadataVersionCheck: Boolean
|
||||||
|
|
||||||
val additionalFeatures: Collection<LanguageFeature>
|
|
||||||
|
|
||||||
@Deprecated("This is a temporary solution, please do not use.")
|
@Deprecated("This is a temporary solution, please do not use.")
|
||||||
val isApiVersionExplicit: Boolean
|
val isApiVersionExplicit: Boolean
|
||||||
}
|
}
|
||||||
@@ -112,19 +121,30 @@ class LanguageVersionSettingsImpl @JvmOverloads constructor(
|
|||||||
override val languageVersion: LanguageVersion,
|
override val languageVersion: LanguageVersion,
|
||||||
override val apiVersion: ApiVersion,
|
override val apiVersion: ApiVersion,
|
||||||
override val skipMetadataVersionCheck: Boolean = false,
|
override val skipMetadataVersionCheck: Boolean = false,
|
||||||
additionalFeatures: Collection<LanguageFeature> = emptySet(),
|
private val specificFeatures: Map<LanguageFeature, LanguageFeature.State> = emptyMap(),
|
||||||
override val isApiVersionExplicit: Boolean = false
|
override val isApiVersionExplicit: Boolean = false
|
||||||
) : LanguageVersionSettings {
|
) : LanguageVersionSettings {
|
||||||
override val additionalFeatures = additionalFeatures.toSet()
|
override fun getFeatureSupport(feature: LanguageFeature): LanguageFeature.State {
|
||||||
|
specificFeatures[feature]?.let { return it }
|
||||||
|
|
||||||
override fun supportsFeature(feature: LanguageFeature): Boolean {
|
|
||||||
val since = feature.sinceVersion
|
val since = feature.sinceVersion
|
||||||
return (since != null && languageVersion >= since && apiVersion >= feature.sinceApiVersion) || feature in additionalFeatures
|
if (since != null && languageVersion >= since && apiVersion >= feature.sinceApiVersion) {
|
||||||
|
return feature.defaultState
|
||||||
|
}
|
||||||
|
|
||||||
|
return LanguageFeature.State.DISABLED
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString() = buildString {
|
override fun toString() = buildString {
|
||||||
append("Language = $languageVersion, API = $apiVersion")
|
append("Language = $languageVersion, API = $apiVersion")
|
||||||
additionalFeatures.forEach { feature -> append(" +$feature") }
|
specificFeatures.forEach { (feature, state) ->
|
||||||
|
val char = when (state) {
|
||||||
|
LanguageFeature.State.ENABLED -> '+'
|
||||||
|
LanguageFeature.State.ENABLED_WITH_WARNING -> '~'
|
||||||
|
LanguageFeature.State.ENABLED_WITH_ERROR, LanguageFeature.State.DISABLED -> '-'
|
||||||
|
}
|
||||||
|
append(" $char$feature")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -132,18 +132,18 @@ private fun getExtraLanguageFeatures(
|
|||||||
coroutineSupport: CoroutineSupport,
|
coroutineSupport: CoroutineSupport,
|
||||||
compilerSettings: CompilerSettings?,
|
compilerSettings: CompilerSettings?,
|
||||||
module: Module?
|
module: Module?
|
||||||
): List<LanguageFeature> {
|
): Map<LanguageFeature, LanguageFeature.State> {
|
||||||
return mutableListOf<LanguageFeature>().apply {
|
return mutableMapOf<LanguageFeature, LanguageFeature.State>().apply {
|
||||||
when (coroutineSupport) {
|
when (coroutineSupport) {
|
||||||
CoroutineSupport.ENABLED -> add(LanguageFeature.DoNotWarnOnCoroutines)
|
CoroutineSupport.ENABLED -> put(LanguageFeature.Coroutines, LanguageFeature.State.ENABLED)
|
||||||
CoroutineSupport.ENABLED_WITH_WARNING -> {}
|
CoroutineSupport.ENABLED_WITH_WARNING -> put(LanguageFeature.Coroutines, LanguageFeature.State.ENABLED_WITH_WARNING)
|
||||||
CoroutineSupport.DISABLED -> add(LanguageFeature.ErrorOnCoroutines)
|
CoroutineSupport.DISABLED -> put(LanguageFeature.Coroutines, LanguageFeature.State.ENABLED_WITH_ERROR)
|
||||||
}
|
}
|
||||||
if (targetPlatformKind == TargetPlatformKind.Common ||
|
if (targetPlatformKind == TargetPlatformKind.Common ||
|
||||||
// TODO: this is a dirty hack, parse arguments correctly here
|
// TODO: this is a dirty hack, parse arguments correctly here
|
||||||
compilerSettings?.additionalArguments?.contains(multiPlatformProjectsArg) == true ||
|
compilerSettings?.additionalArguments?.contains(multiPlatformProjectsArg) == true ||
|
||||||
(module != null && module.implementsCommonModule)) {
|
(module != null && module.implementsCommonModule)) {
|
||||||
add(LanguageFeature.MultiPlatformProjects)
|
put(LanguageFeature.MultiPlatformProjects, LanguageFeature.State.ENABLED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ sealed class TargetPlatformKind<out Version : TargetPlatformVersion>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: merge with LanguageFeature.State
|
||||||
enum class CoroutineSupport(
|
enum class CoroutineSupport(
|
||||||
override val description: String,
|
override val description: String,
|
||||||
val compilerArgument: String
|
val compilerArgument: String
|
||||||
|
|||||||
@@ -46,9 +46,9 @@ class LanguageFeatureQuickFixTest : LightPlatformCodeInsightFixtureTestCase() {
|
|||||||
myFixture.configureByText("foo.kt", "suspend fun foo()")
|
myFixture.configureByText("foo.kt", "suspend fun foo()")
|
||||||
|
|
||||||
assertFalse(KotlinCommonCompilerArgumentsHolder.getInstance(project).settings.coroutinesEnable)
|
assertFalse(KotlinCommonCompilerArgumentsHolder.getInstance(project).settings.coroutinesEnable)
|
||||||
assertFalse(LanguageFeature.DoNotWarnOnCoroutines in project.getLanguageVersionSettings().additionalFeatures)
|
assertEquals(LanguageFeature.State.ENABLED_WITH_WARNING, coroutineSupport)
|
||||||
myFixture.launchAction(myFixture.findSingleIntention("Enable coroutine support in the project"))
|
myFixture.launchAction(myFixture.findSingleIntention("Enable coroutine support in the project"))
|
||||||
assertTrue(LanguageFeature.DoNotWarnOnCoroutines in project.getLanguageVersionSettings().additionalFeatures)
|
assertEquals(LanguageFeature.State.ENABLED, coroutineSupport)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testDisableCoroutines() {
|
fun testDisableCoroutines() {
|
||||||
@@ -57,9 +57,9 @@ class LanguageFeatureQuickFixTest : LightPlatformCodeInsightFixtureTestCase() {
|
|||||||
myFixture.configureByText("foo.kt", "suspend fun foo()")
|
myFixture.configureByText("foo.kt", "suspend fun foo()")
|
||||||
|
|
||||||
assertFalse(KotlinCommonCompilerArgumentsHolder.getInstance(project).settings.coroutinesEnable)
|
assertFalse(KotlinCommonCompilerArgumentsHolder.getInstance(project).settings.coroutinesEnable)
|
||||||
assertFalse(LanguageFeature.ErrorOnCoroutines in project.getLanguageVersionSettings().additionalFeatures)
|
assertEquals(LanguageFeature.State.ENABLED_WITH_WARNING, coroutineSupport)
|
||||||
myFixture.launchAction(myFixture.findSingleIntention("Disable coroutine support in the project"))
|
myFixture.launchAction(myFixture.findSingleIntention("Disable coroutine support in the project"))
|
||||||
assertTrue(LanguageFeature.ErrorOnCoroutines in project.getLanguageVersionSettings().additionalFeatures)
|
assertEquals(LanguageFeature.State.ENABLED_WITH_ERROR, coroutineSupport)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testEnableCoroutinesFacet() {
|
fun testEnableCoroutinesFacet() {
|
||||||
@@ -78,9 +78,9 @@ class LanguageFeatureQuickFixTest : LightPlatformCodeInsightFixtureTestCase() {
|
|||||||
resetProjectSettings(LanguageVersion.KOTLIN_1_1)
|
resetProjectSettings(LanguageVersion.KOTLIN_1_1)
|
||||||
myFixture.configureByText("foo.kt", "suspend fun foo()")
|
myFixture.configureByText("foo.kt", "suspend fun foo()")
|
||||||
|
|
||||||
assertFalse(LanguageFeature.DoNotWarnOnCoroutines in project.getLanguageVersionSettings().additionalFeatures)
|
assertEquals(LanguageFeature.State.ENABLED_WITH_WARNING, coroutineSupport)
|
||||||
myFixture.launchAction(myFixture.findSingleIntention("Enable coroutine support in the project"))
|
myFixture.launchAction(myFixture.findSingleIntention("Enable coroutine support in the project"))
|
||||||
assertTrue(LanguageFeature.DoNotWarnOnCoroutines in project.getLanguageVersionSettings().additionalFeatures)
|
assertEquals(LanguageFeature.State.ENABLED, coroutineSupport)
|
||||||
assertEquals(bundledRuntimeVersion(), JavaRuntimeDetectionUtil.getJavaRuntimeVersion(listOf(runtime)))
|
assertEquals(bundledRuntimeVersion(), JavaRuntimeDetectionUtil.getJavaRuntimeVersion(listOf(runtime)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,6 +175,9 @@ class LanguageFeatureQuickFixTest : LightPlatformCodeInsightFixtureTestCase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val coroutineSupport: LanguageFeature.State
|
||||||
|
get() = project.getLanguageVersionSettings().getFeatureSupport(LanguageFeature.Coroutines)
|
||||||
|
|
||||||
override fun tearDown() {
|
override fun tearDown() {
|
||||||
FacetManager.getInstance(myModule).getFacetByType(KotlinFacetType.TYPE_ID)?.let {
|
FacetManager.getInstance(myModule).getFacetByType(KotlinFacetType.TYPE_ID)?.let {
|
||||||
FacetUtil.deleteFacet(it)
|
FacetUtil.deleteFacet(it)
|
||||||
|
|||||||
Reference in New Issue
Block a user