From 71fcb07fada532a29d8bd7371b0a435196fbbea2 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 15 Feb 2017 19:19:46 +0300 Subject: [PATCH] Infer API version from older runtime in compiler's classpath For example, if you invoke kotlinc 1.1 with kotlin-stdlib 1.0 in the classpath, we now infer -api-version 1.0 automatically --- .../kotlin/cli/common/CLICompiler.java | 8 ++- .../JvmRuntimeVersionsConsistencyChecker.kt | 67 +++++++++++++++---- .../cli/jvm/compiler/KotlinCoreEnvironment.kt | 2 +- .../kotlin/checkers/BaseDiagnosticsTest.kt | 7 +- .../kotlin/config/LanguageVersionSettings.kt | 11 ++- 5 files changed, 77 insertions(+), 18 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 0c17781ae0a..ae2c3b425ce 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.java @@ -44,6 +44,7 @@ import org.jetbrains.kotlin.utils.StringsKt; import java.io.PrintStream; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Properties; @@ -280,7 +281,12 @@ public abstract class CLICompiler { configuration.put( CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, - new LanguageVersionSettingsImpl(languageVersion, ApiVersion.createByLanguageVersion(apiVersion), extraLanguageFeatures) + new LanguageVersionSettingsImpl( + languageVersion, + ApiVersion.createByLanguageVersion(apiVersion), + extraLanguageFeatures, + arguments.apiVersion != 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 4ce0cadc2a1..43a4a86d6e7 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/JvmRuntimeVersionsConsistencyChecker.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/JvmRuntimeVersionsConsistencyChecker.kt @@ -19,12 +19,11 @@ package org.jetbrains.kotlin.cli.jvm import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.cli.common.CLICompiler import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.config.ApiVersion -import org.jetbrains.kotlin.config.LanguageVersionSettings -import org.jetbrains.kotlin.config.MavenComparableVersion +import org.jetbrains.kotlin.config.* import java.io.IOException import java.util.* import java.util.jar.Attributes @@ -113,29 +112,71 @@ object JvmRuntimeVersionsConsistencyChecker { fun checkCompilerClasspathConsistency( messageCollector: MessageCollector, - languageVersionSettings: LanguageVersionSettings?, + configuration: CompilerConfiguration, classpathJarRoots: List ) { val runtimeJarsInfo = collectRuntimeJarsInfo(classpathJarRoots) if (runtimeJarsInfo.jars.isEmpty()) return + val languageVersionSettings = configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS) val apiVersion = languageVersionSettings?.apiVersion?.version ?: CURRENT_COMPILER_VERSION val consistency = checkCompilerClasspathConsistency(messageCollector, apiVersion, runtimeJarsInfo) - if (consistency != ClasspathConsistency.Consistent) { - val message = when (consistency) { - is ClasspathConsistency.InconsistentWithApiVersion -> - "Runtime JAR files in the classpath have the version ${consistency.actualRuntimeVersion}, " + + if (consistency is ClasspathConsistency.InconsistentWithApiVersion) { + val actualRuntimeVersion = consistency.actualRuntimeVersion + messageCollector.issue( + null, + "Runtime JAR files in the classpath have the version $actualRuntimeVersion, " + "which is older than the API version $apiVersion. " + - "Consider removing them from the classpath or passing '-api-version ${consistency.actualRuntimeVersion}' explicitly. " + - "You can also pass '-language-version ${consistency.actualRuntimeVersion}' instead, which will restrict " + + "Consider using the runtime of version $apiVersion, or pass '-api-version $actualRuntimeVersion' explicitly to " + + "restrict the available APIs to the runtime of version $actualRuntimeVersion. " + + "You can also pass '-language-version $actualRuntimeVersion' instead, which will restrict " + "not only the APIs to the specified version, but also the language features. " + "Alternatively, you can use '-Xskip-runtime-version-check' to suppress this warning" - else -> + ) + + val actualApi = ApiVersion.parse(actualRuntimeVersion.toString()) + if (actualApi != null) { + val newSettings = if (languageVersionSettings == null) { + LanguageVersionSettingsImpl( + LanguageVersionSettingsImpl.DEFAULT.languageVersion, + actualApi, + listOf(LanguageFeature.WarnOnCoroutines) + ) + } + else { + val inferredApiVersion = + if (@Suppress("DEPRECATION") languageVersionSettings.isApiVersionExplicit) + languageVersionSettings.apiVersion + else + 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 + LanguageVersionSettingsImpl( + languageVersionSettings.languageVersion, + inferredApiVersion, + languageVersionSettings.additionalFeatures, + isApiVersionExplicit = false + ) + } + + messageCollector.issue(null, "Old runtime has been found in the classpath. " + + "Initial language version settings: $languageVersionSettings. " + + "Updated language version settings: $newSettings", CompilerMessageSeverity.LOGGING) + + configuration.put(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS, newSettings) + } + else { + messageCollector.issue(null, "Could not parse runtime JAR version: $actualRuntimeVersion") + } + } + else if (consistency != ClasspathConsistency.Consistent) { + messageCollector.issue( + null, "Some runtime JAR files in the classpath have an incompatible version. " + "Consider removing them from the classpath or use '-Xskip-runtime-version-check' to suppress this warning" - } - messageCollector.issue(null, message) + ) } val librariesWithBundled = runtimeJarsInfo.otherLibrariesWithBundledRuntime diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt index b76361fe27d..b3ae1d26ba9 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt @@ -159,7 +159,7 @@ class KotlinCoreEnvironment private constructor( if (messageCollector != null) { JvmRuntimeVersionsConsistencyChecker.checkCompilerClasspathConsistency( messageCollector, - configuration.get(CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS), + configuration, initialRoots.mapNotNull { (file, type) -> if (type == JavaRoot.RootType.BINARY) file else null } ) } diff --git a/compiler/tests-common/org/jetbrains/kotlin/checkers/BaseDiagnosticsTest.kt b/compiler/tests-common/org/jetbrains/kotlin/checkers/BaseDiagnosticsTest.kt index c2356fd6458..8964908f50e 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/checkers/BaseDiagnosticsTest.kt +++ b/compiler/tests-common/org/jetbrains/kotlin/checkers/BaseDiagnosticsTest.kt @@ -36,7 +36,6 @@ import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.MultiTargetPlatform -import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.utils.addIfNotNull import org.junit.Assert @@ -108,6 +107,12 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava + get() = error("Must not be called") + + override val isApiVersionExplicit: Boolean + get() = error("Must not be called") } inner class TestFile( diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index ea8e151711f..653832ddcdb 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -99,14 +99,21 @@ interface LanguageVersionSettings { // Please do not use this to enable/disable specific features/checks. Instead add a new LanguageFeature entry and call supportsFeature val languageVersion: LanguageVersion + + // TODO: refactor arguments related to coroutines so that this list is empty by default + val additionalFeatures: Collection + + @Deprecated("This is a temporary solution, please do not use.") + val isApiVersionExplicit: Boolean } class LanguageVersionSettingsImpl @JvmOverloads constructor( override val languageVersion: LanguageVersion, override val apiVersion: ApiVersion, - additionalFeatures: Collection = emptySet() + additionalFeatures: Collection = emptySet(), + override val isApiVersionExplicit: Boolean = false ) : LanguageVersionSettings { - private val additionalFeatures = additionalFeatures.toSet() + override val additionalFeatures = additionalFeatures.toSet() override fun supportsFeature(feature: LanguageFeature): Boolean { val since = feature.sinceVersion