From 32826c168638f516bdb764b28c90d7bf3f9f5c13 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 13 Mar 2017 13:04:06 +0300 Subject: [PATCH] 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 --- .../kotlin/cli/common/CLICompiler.java | 21 ++++---- .../JvmRuntimeVersionsConsistencyChecker.kt | 14 ++---- .../analyzer/common/DefaultAnalyzerFacade.kt | 3 +- .../kotlin/resolve/ModifiersChecker.kt | 20 ++------ .../calls/checkers/coroutineCallChecker.kt | 20 ++++---- compiler/testData/diagnostics/ReadMe.md | 5 +- .../coroutines/coroutinesDisabled.kt | 8 ++-- .../coroutinesEnabledWithWarning.kt | 2 +- .../suspendCoroutineUnavailableWithNewAPI.kt | 2 +- .../checkers/AbstractDiagnosticsTest.kt | 2 +- .../kotlin/checkers/BaseDiagnosticsTest.kt | 32 +++++++------ .../kotlin/config/LanguageVersionSettings.kt | 48 +++++++++++++------ .../jetbrains/kotlin/idea/project/Platform.kt | 12 ++--- .../kotlin/config/KotlinFacetSettings.kt | 1 + .../quickfix/LanguageFeatureQuickFixTest.kt | 15 +++--- 15 files changed, 113 insertions(+), 92 deletions(-) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.java b/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.java index 2d81981e8c2..ae1dd2c445c 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.java @@ -40,8 +40,9 @@ import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStat import org.jetbrains.kotlin.utils.StringsKt; import java.io.PrintStream; -import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Properties; import static org.jetbrains.kotlin.cli.common.ExitCode.*; @@ -261,17 +262,17 @@ public abstract class CLICompiler { ); } - List extraLanguageFeatures = new ArrayList(0); + Map extraLanguageFeatures = new HashMap(0); if (arguments.multiPlatform) { - extraLanguageFeatures.add(LanguageFeature.MultiPlatformProjects); + extraLanguageFeatures.put(LanguageFeature.MultiPlatformProjects, LanguageFeature.State.ENABLED); } if (arguments.noCheckImpl) { - extraLanguageFeatures.add(LanguageFeature.MultiPlatformDoNotCheckImpl); + extraLanguageFeatures.put(LanguageFeature.MultiPlatformDoNotCheckImpl, LanguageFeature.State.ENABLED); } - LanguageFeature coroutinesApplicabilityLevel = chooseCoroutinesApplicabilityLevel(configuration, arguments); - if (coroutinesApplicabilityLevel != null) { - extraLanguageFeatures.add(coroutinesApplicabilityLevel); + LanguageFeature.State coroutinesState = chooseCoroutinesApplicabilityLevel(configuration, arguments); + if (coroutinesState != null) { + extraLanguageFeatures.put(LanguageFeature.Coroutines, coroutinesState); } CommonConfigurationKeysKt.setLanguageVersionSettings( @@ -287,15 +288,15 @@ public abstract class CLICompiler { } @Nullable - private static LanguageFeature chooseCoroutinesApplicabilityLevel( + private static LanguageFeature.State chooseCoroutinesApplicabilityLevel( @NotNull CompilerConfiguration configuration, @NotNull CommonCompilerArguments arguments ) { if (arguments.coroutinesError && !arguments.coroutinesWarn && !arguments.coroutinesEnable) { - return LanguageFeature.ErrorOnCoroutines; + return LanguageFeature.State.ENABLED_WITH_ERROR; } else if (arguments.coroutinesEnable && !arguments.coroutinesWarn && !arguments.coroutinesError) { - return LanguageFeature.DoNotWarnOnCoroutines; + return LanguageFeature.State.ENABLED; } else if (!arguments.coroutinesEnable && !arguments.coroutinesError) { return null; diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/JvmRuntimeVersionsConsistencyChecker.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/JvmRuntimeVersionsConsistencyChecker.kt index 9e569358457..99d92c90877 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/JvmRuntimeVersionsConsistencyChecker.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/JvmRuntimeVersionsConsistencyChecker.kt @@ -123,17 +123,13 @@ object JvmRuntimeVersionsConsistencyChecker { if (@Suppress("DEPRECATION") languageVersionSettings.isApiVersionExplicit) languageVersionSettings.apiVersion 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" 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 - val newSettings = LanguageVersionSettingsImpl( - languageVersionSettings.languageVersion, - inferredApiVersion, - languageVersionSettings.skipMetadataVersionCheck, - languageVersionSettings.additionalFeatures, - isApiVersionExplicit = false - ) + val newSettings = object : LanguageVersionSettings by languageVersionSettings { + override val apiVersion: ApiVersion get() = inferredApiVersion + } messageCollector.issue(null, "Old runtime has been found in the classpath. " + "Initial language version settings: $languageVersionSettings. " + diff --git a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/common/DefaultAnalyzerFacade.kt b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/common/DefaultAnalyzerFacade.kt index b9c2dde8335..a23a5534c98 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/common/DefaultAnalyzerFacade.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/common/DefaultAnalyzerFacade.kt @@ -49,7 +49,8 @@ import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragmen */ object DefaultAnalyzerFacade : AnalyzerFacade() { 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( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt index bbd489f3944..d0e6dea0cff 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.kt @@ -99,14 +99,6 @@ object ModifierCheckerCore { IMPL_KEYWORD to LanguageFeature.MultiPlatformProjects ) - val errorOnFeature = mapOf( - LanguageFeature.Coroutines to LanguageFeature.ErrorOnCoroutines - ) - - val noWarningOnFeature = mapOf( - LanguageFeature.Coroutines to LanguageFeature.DoNotWarnOnCoroutines - ) - val featureDependenciesTargets = mapOf( LanguageFeature.InlineProperties to setOf(PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER) ) @@ -280,27 +272,25 @@ object ModifierCheckerCore { val dependency = featureDependencies[modifier] ?: return true - val errorOnDependencyFeature = errorOnFeature[dependency]?.let { languageVersionSettings.supportsFeature(it) } ?: false - val supportsFeature = languageVersionSettings.supportsFeature(dependency) + val featureSupport = languageVersionSettings.getFeatureSupport(dependency) val diagnosticData = dependency to languageVersionSettings - if (!supportsFeature || errorOnDependencyFeature) { + if (featureSupport == LanguageFeature.State.ENABLED_WITH_ERROR || featureSupport == LanguageFeature.State.DISABLED) { val restrictedTargets = featureDependenciesTargets[dependency] if (restrictedTargets != null && actualTargets.intersect(restrictedTargets).isEmpty()) { return true } - if (!supportsFeature) { + if (featureSupport == LanguageFeature.State.DISABLED) { trace.report(Errors.UNSUPPORTED_FEATURE.on(node.psi, diagnosticData)) } - else if (errorOnDependencyFeature) { + else { trace.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(node.psi, diagnosticData)) } return false } - val pairedNoWarningFeature = noWarningOnFeature[dependency] - if (pairedNoWarningFeature != null && !languageVersionSettings.supportsFeature(pairedNoWarningFeature)) { + if (featureSupport == LanguageFeature.State.ENABLED_WITH_WARNING) { trace.report(Errors.EXPERIMENTAL_FEATURE_WARNING.on(node.psi, diagnosticData)) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt index de828e2ddce..2dd1295cd72 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt @@ -90,14 +90,18 @@ object BuilderFunctionsCallChecker : CallChecker { fun checkCoroutinesFeature(languageVersionSettings: LanguageVersionSettings, diagnosticHolder: DiagnosticSink, reportOn: PsiElement) { val diagnosticData = LanguageFeature.Coroutines to languageVersionSettings - if (!languageVersionSettings.supportsFeature(LanguageFeature.Coroutines)) { - diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(reportOn, diagnosticData)) - } - else if (languageVersionSettings.supportsFeature(LanguageFeature.ErrorOnCoroutines)) { - diagnosticHolder.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(reportOn, diagnosticData)) - } - else if (!languageVersionSettings.supportsFeature(LanguageFeature.DoNotWarnOnCoroutines)) { - diagnosticHolder.report(Errors.EXPERIMENTAL_FEATURE_WARNING.on(reportOn, diagnosticData)) + when (languageVersionSettings.getFeatureSupport(LanguageFeature.Coroutines)) { + LanguageFeature.State.ENABLED -> { + } + LanguageFeature.State.ENABLED_WITH_WARNING -> { + diagnosticHolder.report(Errors.EXPERIMENTAL_FEATURE_WARNING.on(reportOn, diagnosticData)) + } + LanguageFeature.State.ENABLED_WITH_ERROR -> { + diagnosticHolder.report(Errors.EXPERIMENTAL_FEATURE_ERROR.on(reportOn, diagnosticData)) + } + LanguageFeature.State.DISABLED -> { + diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(reportOn, diagnosticData)) + } } } diff --git a/compiler/testData/diagnostics/ReadMe.md b/compiler/testData/diagnostics/ReadMe.md index d8409e542ff..8904876a1ce 100644 --- a/compiler/testData/diagnostics/ReadMe.md +++ b/compiler/testData/diagnostics/ReadMe.md @@ -76,7 +76,8 @@ The directive lets you compose a test consisting of several files in one actual ### 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: @@ -84,6 +85,8 @@ This directive lets you enable or disable certain language features. Language fe // !LANGUAGE: +TypeAliases -LocalDelegatedProperties + // !LANGUAGE: warn:Coroutines + ### 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. diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutinesDisabled.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutinesDisabled.kt index d240e14f2c9..3c307aecace 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutinesDisabled.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutinesDisabled.kt @@ -1,16 +1,16 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER -// !LANGUAGE: +ErrorOnCoroutines +// !LANGUAGE: -Coroutines -suspend fun suspendHere(): String = "OK" +suspend fun suspendHere(): String = "OK" -fun builder(c: suspend () -> Unit) { +fun builder(c: suspend () -> Unit) { } fun box(): String { var result = "" - builder { + builder { suspendHere() } diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutinesEnabledWithWarning.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutinesEnabledWithWarning.kt index 7d7e5691a66..1dcb73b9737 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutinesEnabledWithWarning.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutinesEnabledWithWarning.kt @@ -1,5 +1,5 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER -// !LANGUAGE: -DoNotWarnOnCoroutines +// !LANGUAGE: warn:Coroutines suspend fun suspendHere(): String = "OK" diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendCoroutineUnavailableWithNewAPI.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendCoroutineUnavailableWithNewAPI.kt index 3cf2f600336..47e3917206e 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendCoroutineUnavailableWithNewAPI.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendCoroutineUnavailableWithNewAPI.kt @@ -1,5 +1,5 @@ // !API_VERSION: 1.1 -// !LANGUAGE: +DoNotWarnOnCoroutines +// !LANGUAGE: +Coroutines // SKIP_TXT import kotlin.coroutines.experimental.* diff --git a/compiler/tests-common/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt b/compiler/tests-common/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt index ec47260106b..86c94ee2ed9 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt +++ b/compiler/tests-common/org/jetbrains/kotlin/checkers/AbstractDiagnosticsTest.kt @@ -217,7 +217,7 @@ abstract class AbstractDiagnosticsTest : BaseDiagnosticsTest() { } return result ?: BaseDiagnosticsTest.DiagnosticTestLanguageVersionSettings( - BaseDiagnosticsTest.DEFAULT_DIAGNOSTIC_TESTS_FEATURES.keysToMap { true }, + BaseDiagnosticsTest.DEFAULT_DIAGNOSTIC_TESTS_FEATURES, LanguageVersionSettingsImpl.DEFAULT.apiVersion, LanguageVersionSettingsImpl.DEFAULT.languageVersion ) diff --git a/compiler/tests-common/org/jetbrains/kotlin/checkers/BaseDiagnosticsTest.kt b/compiler/tests-common/org/jetbrains/kotlin/checkers/BaseDiagnosticsTest.kt index be03dc09981..437f2aba752 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/checkers/BaseDiagnosticsTest.kt +++ b/compiler/tests-common/org/jetbrains/kotlin/checkers/BaseDiagnosticsTest.kt @@ -99,20 +99,17 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava, + private val languageFeatures: Map, override val apiVersion: ApiVersion, override val languageVersion: LanguageVersion ) : LanguageVersionSettings { private val delegate = LanguageVersionSettingsImpl(languageVersion, apiVersion) - override fun supportsFeature(feature: LanguageFeature): Boolean = - languageFeatures[feature] ?: delegate.supportsFeature(feature) + override fun getFeatureSupport(feature: LanguageFeature): LanguageFeature.State = + languageFeatures[feature] ?: delegate.getFeatureSupport(feature) override val skipMetadataVersionCheck: Boolean get() = false - override val additionalFeatures: Collection - get() = error("Must not be called") - override val isApiVersionExplicit: Boolean get() = error("Must not be called") } @@ -286,10 +283,10 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava { + private fun collectLanguageFeatureMap(directives: String): Map { val matcher = LANGUAGE_PATTERN.matcher(directives) if (!matcher.find()) { Assert.fail( "Wrong syntax in the '// !$LANGUAGE_DIRECTIVE: ...' directive:\n" + "found: '$directives'\n" + - "Must be '([+-]LanguageFeatureName)+'\n" + - "where '+' means 'enable' and '-' means 'disable'\n" + + "Must be '((+|-|warn:)LanguageFeatureName)+'\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" ) } - val values = HashMap() + val values = HashMap() 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 feature = LanguageFeature.fromString(name) ?: throw AssertionError( "Language feature not found, please check spelling: $name\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") } } diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 1e9d0f22160..54beb75cf76 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -22,14 +22,14 @@ import org.jetbrains.kotlin.utils.DescriptionAware enum class LanguageFeature( val sinceVersion: LanguageVersion?, 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) TypeAliases(KOTLIN_1_1), BoundCallableReferences(KOTLIN_1_1, ApiVersion.KOTLIN_1_1), LocalDelegatedProperties(KOTLIN_1_1, ApiVersion.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), DataClassInheritance(KOTLIN_1_1), InlineProperties(KOTLIN_1_1), @@ -49,11 +49,12 @@ enum class LanguageFeature( DefaultImportOfPackageKotlinComparisons(KOTLIN_1_1), // Experimental features - MultiPlatformProjects(null), - MultiPlatformDoNotCheckImpl(null), - DoNotWarnOnCoroutines(null), - ErrorOnCoroutines(null) + Coroutines(KOTLIN_1_1, ApiVersion.KOTLIN_1_1, "https://kotlinlang.org/docs/diagnostics/experimental-coroutines", State.ENABLED_WITH_WARNING), + + MultiPlatformProjects(null, defaultState = State.DISABLED), + MultiPlatformDoNotCheckImpl(null, defaultState = State.DISABLED), + ; val presentableName: String @@ -62,6 +63,13 @@ enum class LanguageFeature( val presentableText get() = if (hintUrl == null) presentableName else "$presentableName (See: $hintUrl)" + enum class State { + ENABLED, + ENABLED_WITH_WARNING, + ENABLED_WITH_ERROR, + DISABLED; + } + companion object { @JvmStatic 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 { - 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 @@ -102,8 +113,6 @@ interface LanguageVersionSettings { val skipMetadataVersionCheck: Boolean - val additionalFeatures: Collection - @Deprecated("This is a temporary solution, please do not use.") val isApiVersionExplicit: Boolean } @@ -112,19 +121,30 @@ class LanguageVersionSettingsImpl @JvmOverloads constructor( override val languageVersion: LanguageVersion, override val apiVersion: ApiVersion, override val skipMetadataVersionCheck: Boolean = false, - additionalFeatures: Collection = emptySet(), + private val specificFeatures: Map = emptyMap(), override val isApiVersionExplicit: Boolean = false ) : 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 - 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 { 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 { 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 6c6b2f9df34..a42ce26e75c 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 @@ -132,18 +132,18 @@ private fun getExtraLanguageFeatures( coroutineSupport: CoroutineSupport, compilerSettings: CompilerSettings?, module: Module? -): List { - return mutableListOf().apply { +): Map { + return mutableMapOf().apply { when (coroutineSupport) { - CoroutineSupport.ENABLED -> add(LanguageFeature.DoNotWarnOnCoroutines) - CoroutineSupport.ENABLED_WITH_WARNING -> {} - CoroutineSupport.DISABLED -> add(LanguageFeature.ErrorOnCoroutines) + CoroutineSupport.ENABLED -> put(LanguageFeature.Coroutines, LanguageFeature.State.ENABLED) + CoroutineSupport.ENABLED_WITH_WARNING -> put(LanguageFeature.Coroutines, LanguageFeature.State.ENABLED_WITH_WARNING) + CoroutineSupport.DISABLED -> put(LanguageFeature.Coroutines, LanguageFeature.State.ENABLED_WITH_ERROR) } if (targetPlatformKind == TargetPlatformKind.Common || // TODO: this is a dirty hack, parse arguments correctly here compilerSettings?.additionalArguments?.contains(multiPlatformProjectsArg) == true || (module != null && module.implementsCommonModule)) { - add(LanguageFeature.MultiPlatformProjects) + put(LanguageFeature.MultiPlatformProjects, LanguageFeature.State.ENABLED) } } } diff --git a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinFacetSettings.kt b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinFacetSettings.kt index 19f449cb599..fbda4ced164 100644 --- a/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinFacetSettings.kt +++ b/idea/idea-jps-common/src/org/jetbrains/kotlin/config/KotlinFacetSettings.kt @@ -50,6 +50,7 @@ sealed class TargetPlatformKind( } } +// TODO: merge with LanguageFeature.State enum class CoroutineSupport( override val description: String, val compilerArgument: String diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/LanguageFeatureQuickFixTest.kt b/idea/tests/org/jetbrains/kotlin/idea/quickfix/LanguageFeatureQuickFixTest.kt index 39ae9edcaab..b06e8bb88ed 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/LanguageFeatureQuickFixTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/LanguageFeatureQuickFixTest.kt @@ -46,9 +46,9 @@ class LanguageFeatureQuickFixTest : LightPlatformCodeInsightFixtureTestCase() { myFixture.configureByText("foo.kt", "suspend fun foo()") 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")) - assertTrue(LanguageFeature.DoNotWarnOnCoroutines in project.getLanguageVersionSettings().additionalFeatures) + assertEquals(LanguageFeature.State.ENABLED, coroutineSupport) } fun testDisableCoroutines() { @@ -57,9 +57,9 @@ class LanguageFeatureQuickFixTest : LightPlatformCodeInsightFixtureTestCase() { myFixture.configureByText("foo.kt", "suspend fun foo()") 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")) - assertTrue(LanguageFeature.ErrorOnCoroutines in project.getLanguageVersionSettings().additionalFeatures) + assertEquals(LanguageFeature.State.ENABLED_WITH_ERROR, coroutineSupport) } fun testEnableCoroutinesFacet() { @@ -78,9 +78,9 @@ class LanguageFeatureQuickFixTest : LightPlatformCodeInsightFixtureTestCase() { resetProjectSettings(LanguageVersion.KOTLIN_1_1) 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")) - assertTrue(LanguageFeature.DoNotWarnOnCoroutines in project.getLanguageVersionSettings().additionalFeatures) + assertEquals(LanguageFeature.State.ENABLED, coroutineSupport) 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() { FacetManager.getInstance(myModule).getFacetByType(KotlinFacetType.TYPE_ID)?.let { FacetUtil.deleteFacet(it)