Fix libraries analysis for case of isReleaseCoroutines feature enabled
#KT-25466 In Progress
This commit is contained in:
@@ -94,7 +94,8 @@ class ResolverForProjectImpl<M : ModuleInfo>(
|
||||
private val delegateResolver: ResolverForProject<M> = EmptyResolverForProject(),
|
||||
private val firstDependency: M? = null,
|
||||
private val packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory,
|
||||
private val invalidateOnOOCB: Boolean = true
|
||||
private val invalidateOnOOCB: Boolean = true,
|
||||
private val isReleaseCoroutines: Boolean? = null
|
||||
) : ResolverForProject<M>() {
|
||||
|
||||
private class ModuleData(
|
||||
@@ -176,7 +177,8 @@ class ResolverForProjectImpl<M : ModuleInfo>(
|
||||
val moduleContent = modulesContent(module)
|
||||
val resolverForModuleFactory = resolverForModuleFactoryByPlatform(module.platform)
|
||||
|
||||
val languageVersionSettings = moduleLanguageSettingsProvider.getLanguageVersionSettings(module, projectContext.project)
|
||||
val languageVersionSettings =
|
||||
moduleLanguageSettingsProvider.getLanguageVersionSettings(module, projectContext.project, isReleaseCoroutines)
|
||||
val targetPlatformVersion = moduleLanguageSettingsProvider.getTargetPlatform(module)
|
||||
|
||||
resolverForModuleFactory.createResolverForModule(
|
||||
@@ -409,12 +411,20 @@ interface PackageOracleFactory {
|
||||
}
|
||||
|
||||
interface LanguageSettingsProvider {
|
||||
fun getLanguageVersionSettings(moduleInfo: ModuleInfo, project: Project): LanguageVersionSettings
|
||||
fun getLanguageVersionSettings(
|
||||
moduleInfo: ModuleInfo,
|
||||
project: Project,
|
||||
isReleaseCoroutines: Boolean? = null
|
||||
): LanguageVersionSettings
|
||||
|
||||
fun getTargetPlatform(moduleInfo: ModuleInfo): TargetPlatformVersion
|
||||
|
||||
object Default : LanguageSettingsProvider {
|
||||
override fun getLanguageVersionSettings(moduleInfo: ModuleInfo, project: Project) = LanguageVersionSettingsImpl.DEFAULT
|
||||
override fun getLanguageVersionSettings(
|
||||
moduleInfo: ModuleInfo,
|
||||
project: Project,
|
||||
isReleaseCoroutines: Boolean?
|
||||
) = LanguageVersionSettingsImpl.DEFAULT
|
||||
|
||||
override fun getTargetPlatform(moduleInfo: ModuleInfo): TargetPlatformVersion = TargetPlatformVersion.NoVersion
|
||||
}
|
||||
|
||||
@@ -87,7 +87,12 @@ object CommonAnalyzerFacade : ResolverForModuleFactory() {
|
||||
modulesContent = { ModuleContent(it, files, GlobalSearchScope.allScope(project)) },
|
||||
modulePlatforms = { MultiTargetPlatform.Common },
|
||||
moduleLanguageSettingsProvider = object : LanguageSettingsProvider {
|
||||
override fun getLanguageVersionSettings(moduleInfo: ModuleInfo, project: Project) = multiplatformLanguageSettings
|
||||
override fun getLanguageVersionSettings(
|
||||
moduleInfo: ModuleInfo,
|
||||
project: Project,
|
||||
isReleaseCoroutines: Boolean?
|
||||
) = multiplatformLanguageSettings
|
||||
|
||||
override fun getTargetPlatform(moduleInfo: ModuleInfo) = TargetPlatformVersion.NoVersion
|
||||
},
|
||||
resolverForModuleFactoryByPlatform = { CommonAnalyzerFacade },
|
||||
|
||||
+24
-5
@@ -61,7 +61,12 @@ internal val LOG = Logger.getInstance(KotlinCacheService::class.java)
|
||||
// For every different instance of these settings we must create a different builtIns instance and thus a different moduleDescriptor graph
|
||||
// since in the current implementation types from one module are leaking into other modules' resolution
|
||||
// meaning that we can't just change those setting on a per module basis
|
||||
data class PlatformAnalysisSettings(val platform: TargetPlatform, val sdk: Sdk?, val isAdditionalBuiltInFeaturesSupported: Boolean)
|
||||
data class PlatformAnalysisSettings(
|
||||
val platform: TargetPlatform, val sdk: Sdk?,
|
||||
val isAdditionalBuiltInFeaturesSupported: Boolean,
|
||||
// Effectively unused as a property. Needed only to distinguish different modes when being put in a map
|
||||
val isReleaseCoroutines: Boolean
|
||||
)
|
||||
|
||||
class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
override fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade {
|
||||
@@ -89,7 +94,10 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
): ProjectResolutionFacade {
|
||||
val sdk = dependenciesModuleInfo.sdk
|
||||
val platform = JvmPlatform // TODO: Js scripts?
|
||||
val settings = PlatformAnalysisSettings(platform, sdk, true)
|
||||
val settings = PlatformAnalysisSettings(
|
||||
platform, sdk, true,
|
||||
LanguageFeature.ReleaseCoroutines.defaultState == LanguageFeature.State.ENABLED
|
||||
)
|
||||
|
||||
val dependenciesForScriptDependencies = listOf(
|
||||
LibraryModificationTracker.getInstance(project),
|
||||
@@ -162,12 +170,24 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
)
|
||||
}
|
||||
|
||||
private fun IdeaModuleInfo.platformSettings(targetPlatform: TargetPlatform) = PlatformAnalysisSettings(
|
||||
targetPlatform, sdk,
|
||||
supportsAdditionalBuiltInsMembers(),
|
||||
isReleaseCoroutines()
|
||||
)
|
||||
|
||||
private fun IdeaModuleInfo.supportsAdditionalBuiltInsMembers(): Boolean {
|
||||
return IDELanguageSettingsProvider
|
||||
.getLanguageVersionSettings(this, project)
|
||||
.supportsFeature(LanguageFeature.AdditionalBuiltInsMembers)
|
||||
}
|
||||
|
||||
private fun IdeaModuleInfo.isReleaseCoroutines(): Boolean {
|
||||
return IDELanguageSettingsProvider
|
||||
.getLanguageVersionSettings(this, project)
|
||||
.supportsFeature(LanguageFeature.ReleaseCoroutines)
|
||||
}
|
||||
|
||||
private fun globalFacade(settings: PlatformAnalysisSettings) =
|
||||
getOrBuildGlobalFacade(settings).facadeForModules
|
||||
|
||||
@@ -184,8 +204,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
// we assume that all files come from the same module
|
||||
val targetPlatform = files.map { TargetPlatformDetector.getPlatform(it) }.toSet().single()
|
||||
val specialModuleInfo = files.map(KtFile::getModuleInfo).toSet().single()
|
||||
val sdk = specialModuleInfo.sdk
|
||||
val settings = PlatformAnalysisSettings(targetPlatform, sdk, specialModuleInfo.supportsAdditionalBuiltInsMembers())
|
||||
val settings = specialModuleInfo.platformSettings(targetPlatform)
|
||||
|
||||
// File copies are created during completion and receive correct modification events through POM.
|
||||
// Dummy files created e.g. by J2K do not receive events.
|
||||
@@ -419,7 +438,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
}
|
||||
|
||||
private fun getResolutionFacadeByModuleInfo(moduleInfo: IdeaModuleInfo, platform: TargetPlatform): ResolutionFacade {
|
||||
val settings = PlatformAnalysisSettings(platform, moduleInfo.sdk, moduleInfo.supportsAdditionalBuiltInsMembers())
|
||||
val settings = moduleInfo.platformSettings(platform)
|
||||
val projectFacade = when (moduleInfo) {
|
||||
is ScriptDependenciesInfo.ForProject,
|
||||
is ScriptDependenciesSourceInfo.ForProject -> facadeForScriptDependenciesForProject
|
||||
|
||||
+2
-1
@@ -154,7 +154,8 @@ internal class ProjectResolutionFacade(
|
||||
delegateResolver = delegateResolverForProject,
|
||||
firstDependency = settings.sdk?.let { SdkInfo(project, it) },
|
||||
packageOracleFactory = ServiceManager.getService(project, IdePackageOracleFactory::class.java),
|
||||
invalidateOnOOCB = invalidateOnOOCB
|
||||
invalidateOnOOCB = invalidateOnOOCB,
|
||||
isReleaseCoroutines = settings.isReleaseCoroutines
|
||||
)
|
||||
|
||||
if (delegateBuiltIns == null && builtIns is JvmBuiltIns) {
|
||||
|
||||
+8
-3
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.cli.common.arguments.Jsr305Parser
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.config.AnalysisFlag
|
||||
import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.config.TargetPlatformVersion
|
||||
@@ -41,10 +40,16 @@ import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
||||
import org.jetbrains.kotlin.utils.Jsr305State
|
||||
|
||||
object IDELanguageSettingsProvider : LanguageSettingsProvider {
|
||||
override fun getLanguageVersionSettings(moduleInfo: ModuleInfo, project: Project): LanguageVersionSettings =
|
||||
override fun getLanguageVersionSettings(
|
||||
moduleInfo: ModuleInfo,
|
||||
project: Project,
|
||||
isReleaseCoroutines: Boolean?
|
||||
): LanguageVersionSettings =
|
||||
when (moduleInfo) {
|
||||
is ModuleSourceInfo -> moduleInfo.module.languageVersionSettings
|
||||
is LibraryInfo -> project.getLanguageVersionSettings(jsr305State = computeJsr305State(project))
|
||||
is LibraryInfo -> project.getLanguageVersionSettings(
|
||||
jsr305State = computeJsr305State(project), isReleaseCoroutines = isReleaseCoroutines
|
||||
)
|
||||
is ScriptModuleInfo -> getVersionLanguageSettingsForScripts(project, moduleInfo.scriptDefinition)
|
||||
is ScriptDependenciesInfo.ForFile -> getVersionLanguageSettingsForScripts(project, moduleInfo.scriptDefinition)
|
||||
is PlatformModuleInfo -> moduleInfo.platformModule.module.languageVersionSettings
|
||||
|
||||
@@ -110,13 +110,14 @@ fun Module.getStableName(): Name {
|
||||
@JvmOverloads
|
||||
fun Project.getLanguageVersionSettings(
|
||||
contextModule: Module? = null,
|
||||
jsr305State: Jsr305State? = null // this is a temporary hack until we'll have a sane way to configure libraries analysis
|
||||
jsr305State: Jsr305State? = null,
|
||||
isReleaseCoroutines: Boolean? = null
|
||||
): LanguageVersionSettings {
|
||||
val arguments = KotlinCommonCompilerArgumentsHolder.getInstance(this).settings
|
||||
val languageVersion =
|
||||
LanguageVersion.fromVersionString(arguments.languageVersion)
|
||||
?: contextModule?.getAndCacheLanguageLevelByDependencies()
|
||||
?: LanguageVersion.LATEST_STABLE
|
||||
?: contextModule?.getAndCacheLanguageLevelByDependencies()
|
||||
?: LanguageVersion.LATEST_STABLE
|
||||
val apiVersion = ApiVersion.createByLanguageVersion(LanguageVersion.fromVersionString(arguments.apiVersion) ?: languageVersion)
|
||||
val compilerSettings = KotlinCompilerSettings.getInstance(this).settings
|
||||
|
||||
@@ -127,6 +128,12 @@ fun Project.getLanguageVersionSettings(
|
||||
|
||||
val extraLanguageFeatures = additionalArguments.configureLanguageFeatures(MessageCollector.NONE).apply {
|
||||
configureCoroutinesSupport(CoroutineSupport.byCompilerArguments(KotlinCommonCompilerArgumentsHolder.getInstance(this@getLanguageVersionSettings).settings))
|
||||
if (isReleaseCoroutines != null) {
|
||||
put(
|
||||
LanguageFeature.ReleaseCoroutines,
|
||||
if (isReleaseCoroutines) LanguageFeature.State.ENABLED else LanguageFeature.State.DISABLED
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val extraAnalysisFlags = additionalArguments.configureAnalysisFlags(MessageCollector.NONE).apply {
|
||||
@@ -146,7 +153,7 @@ val Module.languageVersionSettings: LanguageVersionSettings
|
||||
get() {
|
||||
val cachedValue =
|
||||
getUserData(LANGUAGE_VERSION_SETTINGS)
|
||||
?: createCachedValueForLanguageVersionSettings().also { putUserData(LANGUAGE_VERSION_SETTINGS, it) }
|
||||
?: createCachedValueForLanguageVersionSettings().also { putUserData(LANGUAGE_VERSION_SETTINGS, it) }
|
||||
|
||||
return cachedValue.value
|
||||
}
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package libN
|
||||
|
||||
suspend fun newFoo() {}
|
||||
fun newBuilder(x: suspend () -> Unit) {}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package libO
|
||||
|
||||
suspend fun oldFoo() {}
|
||||
fun oldBuilder(x: suspend () -> Unit) {}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import libN.*
|
||||
import libO.*
|
||||
|
||||
suspend fun newMain() {
|
||||
newFoo()
|
||||
oldFoo(<error descr="[NO_VALUE_FOR_PARAMETER] No value passed for parameter 'continuation'">)</error>
|
||||
|
||||
oldFoo(
|
||||
object : kotlin.coroutines.experimental.Continuation<Unit> {
|
||||
override val context
|
||||
get() = null!!
|
||||
|
||||
override fun resume(value: Unit) {}
|
||||
|
||||
override fun resumeWithException(exception: Throwable) {}
|
||||
}
|
||||
)
|
||||
|
||||
// TODO: actually, it's a bug
|
||||
oldMain()
|
||||
}
|
||||
|
||||
fun newMain2() {
|
||||
newBuilder {
|
||||
newMain()
|
||||
}
|
||||
|
||||
oldBuilder {
|
||||
<error descr="[ILLEGAL_SUSPEND_FUNCTION_CALL] Suspend function 'newMain' should be called only from a coroutine or another suspend function">newMain</error>()
|
||||
|
||||
// `suspend () -> Unit` becomes (Continuation<Unit> -> Any?)
|
||||
it.resume(Unit)
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import libN.*
|
||||
import libO.*
|
||||
|
||||
suspend fun oldMain() {
|
||||
<error descr="[VERSION_REQUIREMENT_DEPRECATION_ERROR] 'newFoo(): Unit' is only available since Kotlin 1.3 and cannot be used in Kotlin 1.2">newFoo</error>()
|
||||
oldFoo()
|
||||
}
|
||||
|
||||
fun oldMain2() {
|
||||
<error descr="[VERSION_REQUIREMENT_DEPRECATION_ERROR] 'newBuilder((Continuation<Unit>) -> Any?): Unit' is only available since Kotlin 1.3 and cannot be used in Kotlin 1.2">newBuilder</error> {
|
||||
<error descr="[ILLEGAL_SUSPEND_FUNCTION_CALL] Suspend function 'oldMain' should be called only from a coroutine or another suspend function">oldMain</error>()
|
||||
}
|
||||
|
||||
oldBuilder {
|
||||
oldMain()
|
||||
}
|
||||
}
|
||||
@@ -30,15 +30,20 @@ import com.intellij.psi.util.PsiModificationTracker
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.analyzer.ResolverForModuleComputationTracker
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersion
|
||||
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
|
||||
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder
|
||||
import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings
|
||||
import org.jetbrains.kotlin.idea.completion.test.withServiceRegistered
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacetConfiguration
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacetType
|
||||
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
||||
import org.jetbrains.kotlin.idea.project.KotlinCodeBlockModificationListener
|
||||
import org.jetbrains.kotlin.idea.project.KotlinModuleModificationTracker
|
||||
import org.jetbrains.kotlin.idea.project.getLanguageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.idea.test.allKotlinFiles
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
@@ -240,6 +245,45 @@ open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
|
||||
checkHighlightingInProject()
|
||||
}
|
||||
|
||||
fun testCoroutineMixedReleaseStatus() {
|
||||
KotlinCommonCompilerArgumentsHolder.getInstance(project).update { skipMetadataVersionCheck = true }
|
||||
KotlinCompilerSettings.getInstance(project).update { additionalArguments = "-Xskip-metadata-version-check" }
|
||||
|
||||
val libOld = MockLibraryUtil.compileJvmLibraryToJar(
|
||||
testDataPath + "${getTestName(true)}/libOld", "libOld",
|
||||
extraOptions = listOf("-language-version", "1.2", "-api-version", "1.2")
|
||||
)
|
||||
|
||||
val libNew = MockLibraryUtil.compileJvmLibraryToJar(
|
||||
testDataPath + "${getTestName(true)}/libNew", "libNew",
|
||||
extraOptions = listOf("-language-version", "1.3", "-api-version", "1.3")
|
||||
)
|
||||
|
||||
val moduleNew = module("moduleNew").setupKotlinFacet {
|
||||
settings.coroutineSupport = LanguageFeature.State.ENABLED
|
||||
settings.languageLevel = LanguageVersion.KOTLIN_1_3
|
||||
settings.apiLevel = LanguageVersion.KOTLIN_1_3
|
||||
}
|
||||
|
||||
val moduleOld = module("moduleOld").setupKotlinFacet {
|
||||
settings.coroutineSupport = LanguageFeature.State.ENABLED
|
||||
settings.languageLevel = LanguageVersion.KOTLIN_1_2
|
||||
settings.apiLevel = LanguageVersion.KOTLIN_1_2
|
||||
}
|
||||
|
||||
moduleNew.addLibrary(libOld)
|
||||
moduleNew.addLibrary(libNew)
|
||||
moduleNew.addLibrary(ForTestCompileRuntime.runtimeJarForTests())
|
||||
|
||||
moduleOld.addLibrary(libNew)
|
||||
moduleOld.addLibrary(libOld)
|
||||
moduleOld.addLibrary(ForTestCompileRuntime.runtimeJarForTests())
|
||||
|
||||
moduleNew.addDependency(moduleOld)
|
||||
|
||||
checkHighlightingInProject()
|
||||
}
|
||||
|
||||
private fun Module.setupKotlinFacet(configure: KotlinFacetConfiguration.() -> Unit) = apply {
|
||||
runWriteAction {
|
||||
val facet = FacetManager.getInstance(this).addFacet(KotlinFacetType.INSTANCE, KotlinFacetType.NAME, null)
|
||||
|
||||
Reference in New Issue
Block a user