Configuration: Check that project-level common arguments are not changed through platform-specific holders

This commit is contained in:
Alexey Sedunov
2017-03-13 20:23:15 +03:00
parent ce434585e3
commit 5c55b9fbbe
13 changed files with 62 additions and 23 deletions
@@ -89,7 +89,7 @@ private fun Any.copyValueIfNeeded(): Any {
}
}
private fun collectFieldsToCopy(clazz: Class<*>, inheritedOnly: Boolean): List<Field> {
fun collectFieldsToCopy(clazz: Class<*>, inheritedOnly: Boolean): List<Field> {
val fromFields = ArrayList<Field>()
var currentClass: Class<*>? = if (inheritedOnly) clazz.superclass else clazz
@@ -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<T : Any> protected constructor() : PersistentStateComponent<Element>, 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()
@@ -48,9 +48,10 @@ class KotlinCommonCompilerArgumentsHolder : BaseKotlinCompilerSettings<CommonCom
super.loadState(state)
// To fix earlier configurations with incorrect combination of language and API version
val settings = settings
if (VersionComparatorUtil.compare(settings.languageVersion, settings.apiVersion) < 0) {
settings.apiVersion = settings.languageVersion
update {
if (VersionComparatorUtil.compare(languageVersion, apiVersion) < 0) {
apiVersion = languageVersion
}
}
}
@@ -27,6 +27,8 @@ import org.jetbrains.kotlin.config.SettingConstants.KOTLIN_COMPILER_SETTINGS_SEC
class KotlinCompilerSettings : BaseKotlinCompilerSettings<CompilerSettings>() {
override fun createSettings() = CompilerSettings()
companion object {
fun getInstance(project: Project) = ServiceManager.getService(project, KotlinCompilerSettings::class.java)!!
}
@@ -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
}
@@ -28,6 +28,10 @@ import org.jetbrains.kotlin.idea.compiler.configuration.BaseKotlinCompilerSettin
class Kotlin2JsCompilerArgumentsHolder : BaseKotlinCompilerSettings<K2JSCompilerArguments>() {
override fun createSettings() = K2JSCompilerArguments.createDefaultInstance()
override fun validateNewSettings(settings: K2JSCompilerArguments) {
validateInheritedFieldsUnchanged(settings)
}
companion object {
fun getInstance(project: Project) = ServiceManager.getService(project, Kotlin2JsCompilerArgumentsHolder::class.java)!!
}
@@ -29,6 +29,10 @@ import org.jetbrains.kotlin.idea.compiler.configuration.BaseKotlinCompilerSettin
class Kotlin2JvmCompilerArgumentsHolder : BaseKotlinCompilerSettings<K2JVMCompilerArguments>() {
override fun createSettings() = K2JVMCompilerArguments.createDefaultInstance()
override fun validateNewSettings(settings: K2JVMCompilerArguments) {
validateInheritedFieldsUnchanged(settings)
}
companion object {
fun getInstance(project: Project) = ServiceManager.getService(project, Kotlin2JvmCompilerArgumentsHolder::class.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);
}
@@ -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
@@ -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
@@ -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 ||
@@ -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
}
@@ -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