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
This commit is contained in:
@@ -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<A extends CommonCompilerArguments> {
|
||||
|
||||
configuration.put(
|
||||
CommonConfigurationKeys.LANGUAGE_VERSION_SETTINGS,
|
||||
new LanguageVersionSettingsImpl(languageVersion, ApiVersion.createByLanguageVersion(apiVersion), extraLanguageFeatures)
|
||||
new LanguageVersionSettingsImpl(
|
||||
languageVersion,
|
||||
ApiVersion.createByLanguageVersion(apiVersion),
|
||||
extraLanguageFeatures,
|
||||
arguments.apiVersion != null
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+54
-13
@@ -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<VirtualFile>
|
||||
) {
|
||||
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
|
||||
|
||||
@@ -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 }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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<TestModule, Tes
|
||||
|
||||
override fun supportsFeature(feature: LanguageFeature): Boolean =
|
||||
languageFeatures[feature] ?: delegate.supportsFeature(feature)
|
||||
|
||||
override val additionalFeatures: Collection<LanguageFeature>
|
||||
get() = error("Must not be called")
|
||||
|
||||
override val isApiVersionExplicit: Boolean
|
||||
get() = error("Must not be called")
|
||||
}
|
||||
|
||||
inner class TestFile(
|
||||
|
||||
@@ -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<LanguageFeature>
|
||||
|
||||
@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<LanguageFeature> = emptySet()
|
||||
additionalFeatures: Collection<LanguageFeature> = 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
|
||||
|
||||
Reference in New Issue
Block a user