From 5c55b9fbbec45bd8a9e00a5e4d6c67522defa8a4 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Mon, 13 Mar 2017 20:23:15 +0300 Subject: [PATCH] Configuration: Check that project-level common arguments are not changed through platform-specific holders --- .../cli/common/arguments/argumentUtils.kt | 2 +- .../BaseKotlinCompilerSettings.kt | 35 +++++++++++++++---- .../KotlinCommonCompilerArgumentsHolder.kt | 7 ++-- .../configuration/KotlinCompilerSettings.kt | 2 ++ .../jetbrains/kotlin/idea/project/Platform.kt | 2 +- .../Kotlin2JsCompilerArgumentsHolder.kt | 4 +++ .../Kotlin2JvmCompilerArgumentsHolder.kt | 4 +++ .../KotlinCompilerConfigurableTab.java | 7 ++++ .../idea/facet/KotlinFacetEditorGeneralTab.kt | 12 +++---- .../jetbrains/kotlin/idea/facet/facetUtils.kt | 2 +- .../quickfix/ChangeCoroutineSupportFix.kt | 2 +- .../quickfix/EnableUnsupportedFeatureFix.kt | 4 +-- .../quickfix/LanguageFeatureQuickFixTest.kt | 2 +- 13 files changed, 62 insertions(+), 23 deletions(-) diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/argumentUtils.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/argumentUtils.kt index 19235311642..46d1ca14de5 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/argumentUtils.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/argumentUtils.kt @@ -89,7 +89,7 @@ private fun Any.copyValueIfNeeded(): Any { } } -private fun collectFieldsToCopy(clazz: Class<*>, inheritedOnly: Boolean): List { +fun collectFieldsToCopy(clazz: Class<*>, inheritedOnly: Boolean): List { val fromFields = ArrayList() var currentClass: Class<*>? = if (inheritedOnly) clazz.superclass else clazz diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/BaseKotlinCompilerSettings.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/BaseKotlinCompilerSettings.kt index c57c49c70a2..97dde831af5 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/BaseKotlinCompilerSettings.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/BaseKotlinCompilerSettings.kt @@ -21,22 +21,43 @@ import com.intellij.openapi.components.StoragePathMacros.PROJECT_CONFIG_DIR import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters import com.intellij.util.xmlb.XmlSerializer import org.jdom.Element -import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments -import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments -import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.cli.common.arguments.* import org.jetbrains.kotlin.config.SettingConstants abstract class BaseKotlinCompilerSettings protected constructor() : PersistentStateComponent, Cloneable { @Suppress("LeakingThis") - var settings: T = createSettings() - private set + private var _settings: T = createSettings() + + var settings: T + get() = copyBean(_settings) + set(value) { + validateNewSettings(value) + _settings = copyBean(value) + } + + fun update(changer: T.() -> Unit) { + settings = settings.apply { changer() } + } + + protected fun validateInheritedFieldsUnchanged(settings: T) { + val inheritedFields = collectFieldsToCopy(settings.javaClass, true) + val defaultInstance = createSettings() + val invalidFields = inheritedFields.filter { it.get(settings) != it.get(defaultInstance) } + if (invalidFields.isNotEmpty()) { + throw IllegalArgumentException("Following fields are expected to be left unchanged in ${settings.javaClass}: ${invalidFields.joinToString { it.name }}") + } + } + + protected open fun validateNewSettings(settings: T) { + + } protected abstract fun createSettings(): T - override fun getState() = XmlSerializer.serialize(settings, SKIP_DEFAULT_VALUES) + override fun getState() = XmlSerializer.serialize(_settings, SKIP_DEFAULT_VALUES) override fun loadState(state: Element) { - settings = XmlSerializer.deserialize(state, settings.javaClass) ?: createSettings() + _settings = XmlSerializer.deserialize(state, _settings.javaClass) ?: createSettings() } public override fun clone(): Any = super.clone() diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCommonCompilerArgumentsHolder.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCommonCompilerArgumentsHolder.kt index 7bd1ce6def1..205be15b671 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCommonCompilerArgumentsHolder.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCommonCompilerArgumentsHolder.kt @@ -48,9 +48,10 @@ class KotlinCommonCompilerArgumentsHolder : BaseKotlinCompilerSettings() { override fun createSettings() = CompilerSettings() + + companion object { fun getInstance(project: Project) = ServiceManager.getService(project, KotlinCompilerSettings::class.java)!! } 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 d10b069b2c2..1ae2b8841c3 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 @@ -53,7 +53,7 @@ fun Module.getAndCacheLanguageLevelByDependencies(): LanguageVersion { // Preserve inferred version in facet/project settings val facetSettings = KotlinFacetSettingsProvider.getInstance(project).getSettings(this) if (facetSettings.useProjectSettings) { - with(KotlinCommonCompilerArgumentsHolder.getInstance(project).settings) { + KotlinCommonCompilerArgumentsHolder.getInstance(project).update { if (languageVersion == null) { languageVersion = languageLevel.versionString } diff --git a/idea/src/org/jetbrains/kotlin/idea/compiler/configuration/Kotlin2JsCompilerArgumentsHolder.kt b/idea/src/org/jetbrains/kotlin/idea/compiler/configuration/Kotlin2JsCompilerArgumentsHolder.kt index da344379a6a..65e78dddac8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/compiler/configuration/Kotlin2JsCompilerArgumentsHolder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/compiler/configuration/Kotlin2JsCompilerArgumentsHolder.kt @@ -28,6 +28,10 @@ import org.jetbrains.kotlin.idea.compiler.configuration.BaseKotlinCompilerSettin class Kotlin2JsCompilerArgumentsHolder : BaseKotlinCompilerSettings() { override fun createSettings() = K2JSCompilerArguments.createDefaultInstance() + override fun validateNewSettings(settings: K2JSCompilerArguments) { + validateInheritedFieldsUnchanged(settings) + } + companion object { fun getInstance(project: Project) = ServiceManager.getService(project, Kotlin2JsCompilerArgumentsHolder::class.java)!! } diff --git a/idea/src/org/jetbrains/kotlin/idea/compiler/configuration/Kotlin2JvmCompilerArgumentsHolder.kt b/idea/src/org/jetbrains/kotlin/idea/compiler/configuration/Kotlin2JvmCompilerArgumentsHolder.kt index 1a4821d80d8..899f881c4ac 100644 --- a/idea/src/org/jetbrains/kotlin/idea/compiler/configuration/Kotlin2JvmCompilerArgumentsHolder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/compiler/configuration/Kotlin2JvmCompilerArgumentsHolder.kt @@ -29,6 +29,10 @@ import org.jetbrains.kotlin.idea.compiler.configuration.BaseKotlinCompilerSettin class Kotlin2JvmCompilerArgumentsHolder : BaseKotlinCompilerSettings() { override fun createSettings() = K2JVMCompilerArguments.createDefaultInstance() + override fun validateNewSettings(settings: K2JVMCompilerArguments) { + validateInheritedFieldsUnchanged(settings) + } + companion object { fun getInstance(project: Project) = ServiceManager.getService(project, Kotlin2JvmCompilerArgumentsHolder::class.java)!! } diff --git a/idea/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCompilerConfigurableTab.java b/idea/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCompilerConfigurableTab.java index af73e23b250..9697b79c4ef 100644 --- a/idea/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCompilerConfigurableTab.java +++ b/idea/src/org/jetbrains/kotlin/idea/compiler/configuration/KotlinCompilerConfigurableTab.java @@ -402,6 +402,13 @@ public class KotlinCompilerConfigurableTab implements SearchableConfigurable, Co k2jvmCompilerArguments.jvmTarget = getSelectedJvmVersion(); + if (isProjectSettings) { + KotlinCommonCompilerArgumentsHolder.Companion.getInstance(project).setSettings(commonCompilerArguments); + Kotlin2JvmCompilerArgumentsHolder.Companion.getInstance(project).setSettings(k2jvmCompilerArguments); + Kotlin2JsCompilerArgumentsHolder.Companion.getInstance(project).setSettings(k2jsCompilerArguments); + KotlinCompilerSettings.Companion.getInstance(project).setSettings(compilerSettings); + } + BuildManager.getInstance().clearState(project); } diff --git a/idea/src/org/jetbrains/kotlin/idea/facet/KotlinFacetEditorGeneralTab.kt b/idea/src/org/jetbrains/kotlin/idea/facet/KotlinFacetEditorGeneralTab.kt index 29957ec115e..167c515479e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/facet/KotlinFacetEditorGeneralTab.kt +++ b/idea/src/org/jetbrains/kotlin/idea/facet/KotlinFacetEditorGeneralTab.kt @@ -66,9 +66,9 @@ class KotlinFacetEditorGeneralTab( else { editableCommonArguments = configuration!!.settings.compilerArguments!! editableJvmArguments = editableCommonArguments as? K2JVMCompilerArguments - ?: copyBean(Kotlin2JvmCompilerArgumentsHolder.getInstance(project).settings) + ?: Kotlin2JvmCompilerArgumentsHolder.getInstance(project).settings editableJsArguments = editableCommonArguments as? K2JSCompilerArguments - ?: copyBean(Kotlin2JsCompilerArgumentsHolder.getInstance(project).settings) + ?: Kotlin2JsCompilerArgumentsHolder.getInstance(project).settings editableCompilerSettings = configuration.settings.compilerSettings!! } @@ -127,10 +127,10 @@ class KotlinFacetEditorGeneralTab( compilerConfigurable.setTargetPlatform(chosenPlatform) compilerConfigurable.setEnabled(!useProjectSettings) if (useProjectSettings) { - compilerConfigurable.commonCompilerArguments = copyBean(KotlinCommonCompilerArgumentsHolder.getInstance(project).settings) - compilerConfigurable.k2jvmCompilerArguments = copyBean(Kotlin2JvmCompilerArgumentsHolder.getInstance(project).settings) - compilerConfigurable.k2jsCompilerArguments = copyBean(Kotlin2JsCompilerArgumentsHolder.getInstance(project).settings) - compilerConfigurable.compilerSettings = copyBean(KotlinCompilerSettings.getInstance(project).settings) + compilerConfigurable.commonCompilerArguments = KotlinCommonCompilerArgumentsHolder.getInstance(project).settings + compilerConfigurable.k2jvmCompilerArguments = Kotlin2JvmCompilerArgumentsHolder.getInstance(project).settings + compilerConfigurable.k2jsCompilerArguments = Kotlin2JsCompilerArgumentsHolder.getInstance(project).settings + compilerConfigurable.compilerSettings = KotlinCompilerSettings.getInstance(project).settings } else { compilerConfigurable.commonCompilerArguments = editableCommonArguments diff --git a/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt b/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt index b60acffc38e..653cef81b8b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt @@ -58,7 +58,7 @@ fun KotlinFacetSettings.initializeIfNeeded( val project = module.project if (compilerSettings == null) { - compilerSettings = copyBean(KotlinCompilerSettings.getInstance(project).settings) + compilerSettings = KotlinCompilerSettings.getInstance(project).settings } val commonArguments = KotlinCommonCompilerArgumentsHolder.getInstance(module.project).settings diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeCoroutineSupportFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeCoroutineSupportFix.kt index 90848e04d18..c23807ed646 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeCoroutineSupportFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeCoroutineSupportFix.kt @@ -94,7 +94,7 @@ sealed class ChangeCoroutineSupportFix( if (!checkUpdateRuntime(project, LanguageFeature.Coroutines.sinceApiVersion)) return } - with(KotlinCommonCompilerArgumentsHolder.getInstance(project).settings) { + KotlinCommonCompilerArgumentsHolder.getInstance(project).update { coroutinesEnable = coroutineSupport == LanguageFeature.State.ENABLED coroutinesWarn = coroutineSupport == LanguageFeature.State.ENABLED_WITH_WARNING coroutinesError = coroutineSupport == LanguageFeature.State.ENABLED_WITH_ERROR || diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/EnableUnsupportedFeatureFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/EnableUnsupportedFeatureFix.kt index b9a22d21d26..299f04eac73 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/EnableUnsupportedFeatureFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/EnableUnsupportedFeatureFix.kt @@ -123,10 +123,10 @@ sealed class EnableUnsupportedFeatureFix( override fun invoke(project: Project, editor: Editor?, file: KtFile) { val targetVersion = feature.sinceVersion!! - with(KotlinCommonCompilerArgumentsHolder.getInstance(project).settings) { + KotlinCommonCompilerArgumentsHolder.getInstance(project).update { val parsedApiVersion = ApiVersion.parse(apiVersion) if (parsedApiVersion != null && feature.sinceApiVersion > parsedApiVersion) { - if (!checkUpdateRuntime(project, feature.sinceApiVersion)) return + if (!checkUpdateRuntime(project, feature.sinceApiVersion)) return@update apiVersion = feature.sinceApiVersion.versionString } diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/LanguageFeatureQuickFixTest.kt b/idea/tests/org/jetbrains/kotlin/idea/quickfix/LanguageFeatureQuickFixTest.kt index e4b54f02a0e..01312a129dd 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/LanguageFeatureQuickFixTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/LanguageFeatureQuickFixTest.kt @@ -165,7 +165,7 @@ class LanguageFeatureQuickFixTest : LightPlatformCodeInsightFixtureTestCase() { } private fun resetProjectSettings(version: LanguageVersion) { - with(KotlinCommonCompilerArgumentsHolder.getInstance(project).settings) { + KotlinCommonCompilerArgumentsHolder.getInstance(project).update { languageVersion = version.versionString apiVersion = version.versionString coroutinesEnable = false