Fix libraries analysis for case of isReleaseCoroutines feature enabled

#KT-25466 In Progress
This commit is contained in:
Denis Zharkov
2018-07-19 16:18:46 +03:00
parent f1463b4a2e
commit f72aa78eec
11 changed files with 168 additions and 18 deletions
@@ -94,7 +94,8 @@ class ResolverForProjectImpl<M : ModuleInfo>(
private val delegateResolver: ResolverForProject<M> = EmptyResolverForProject(), private val delegateResolver: ResolverForProject<M> = EmptyResolverForProject(),
private val firstDependency: M? = null, private val firstDependency: M? = null,
private val packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory, private val packageOracleFactory: PackageOracleFactory = PackageOracleFactory.OptimisticFactory,
private val invalidateOnOOCB: Boolean = true private val invalidateOnOOCB: Boolean = true,
private val isReleaseCoroutines: Boolean? = null
) : ResolverForProject<M>() { ) : ResolverForProject<M>() {
private class ModuleData( private class ModuleData(
@@ -176,7 +177,8 @@ class ResolverForProjectImpl<M : ModuleInfo>(
val moduleContent = modulesContent(module) val moduleContent = modulesContent(module)
val resolverForModuleFactory = resolverForModuleFactoryByPlatform(module.platform) 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) val targetPlatformVersion = moduleLanguageSettingsProvider.getTargetPlatform(module)
resolverForModuleFactory.createResolverForModule( resolverForModuleFactory.createResolverForModule(
@@ -409,12 +411,20 @@ interface PackageOracleFactory {
} }
interface LanguageSettingsProvider { interface LanguageSettingsProvider {
fun getLanguageVersionSettings(moduleInfo: ModuleInfo, project: Project): LanguageVersionSettings fun getLanguageVersionSettings(
moduleInfo: ModuleInfo,
project: Project,
isReleaseCoroutines: Boolean? = null
): LanguageVersionSettings
fun getTargetPlatform(moduleInfo: ModuleInfo): TargetPlatformVersion fun getTargetPlatform(moduleInfo: ModuleInfo): TargetPlatformVersion
object Default : LanguageSettingsProvider { 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 override fun getTargetPlatform(moduleInfo: ModuleInfo): TargetPlatformVersion = TargetPlatformVersion.NoVersion
} }
@@ -87,7 +87,12 @@ object CommonAnalyzerFacade : ResolverForModuleFactory() {
modulesContent = { ModuleContent(it, files, GlobalSearchScope.allScope(project)) }, modulesContent = { ModuleContent(it, files, GlobalSearchScope.allScope(project)) },
modulePlatforms = { MultiTargetPlatform.Common }, modulePlatforms = { MultiTargetPlatform.Common },
moduleLanguageSettingsProvider = object : LanguageSettingsProvider { 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 override fun getTargetPlatform(moduleInfo: ModuleInfo) = TargetPlatformVersion.NoVersion
}, },
resolverForModuleFactoryByPlatform = { CommonAnalyzerFacade }, resolverForModuleFactoryByPlatform = { CommonAnalyzerFacade },
@@ -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 // 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 // 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 // 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 { class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
override fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade { override fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade {
@@ -89,7 +94,10 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
): ProjectResolutionFacade { ): ProjectResolutionFacade {
val sdk = dependenciesModuleInfo.sdk val sdk = dependenciesModuleInfo.sdk
val platform = JvmPlatform // TODO: Js scripts? 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( val dependenciesForScriptDependencies = listOf(
LibraryModificationTracker.getInstance(project), 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 { private fun IdeaModuleInfo.supportsAdditionalBuiltInsMembers(): Boolean {
return IDELanguageSettingsProvider return IDELanguageSettingsProvider
.getLanguageVersionSettings(this, project) .getLanguageVersionSettings(this, project)
.supportsFeature(LanguageFeature.AdditionalBuiltInsMembers) .supportsFeature(LanguageFeature.AdditionalBuiltInsMembers)
} }
private fun IdeaModuleInfo.isReleaseCoroutines(): Boolean {
return IDELanguageSettingsProvider
.getLanguageVersionSettings(this, project)
.supportsFeature(LanguageFeature.ReleaseCoroutines)
}
private fun globalFacade(settings: PlatformAnalysisSettings) = private fun globalFacade(settings: PlatformAnalysisSettings) =
getOrBuildGlobalFacade(settings).facadeForModules getOrBuildGlobalFacade(settings).facadeForModules
@@ -184,8 +204,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
// we assume that all files come from the same module // we assume that all files come from the same module
val targetPlatform = files.map { TargetPlatformDetector.getPlatform(it) }.toSet().single() val targetPlatform = files.map { TargetPlatformDetector.getPlatform(it) }.toSet().single()
val specialModuleInfo = files.map(KtFile::getModuleInfo).toSet().single() val specialModuleInfo = files.map(KtFile::getModuleInfo).toSet().single()
val sdk = specialModuleInfo.sdk val settings = specialModuleInfo.platformSettings(targetPlatform)
val settings = PlatformAnalysisSettings(targetPlatform, sdk, specialModuleInfo.supportsAdditionalBuiltInsMembers())
// File copies are created during completion and receive correct modification events through POM. // File copies are created during completion and receive correct modification events through POM.
// Dummy files created e.g. by J2K do not receive events. // 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 { 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) { val projectFacade = when (moduleInfo) {
is ScriptDependenciesInfo.ForProject, is ScriptDependenciesInfo.ForProject,
is ScriptDependenciesSourceInfo.ForProject -> facadeForScriptDependenciesForProject is ScriptDependenciesSourceInfo.ForProject -> facadeForScriptDependenciesForProject
@@ -154,7 +154,8 @@ internal class ProjectResolutionFacade(
delegateResolver = delegateResolverForProject, delegateResolver = delegateResolverForProject,
firstDependency = settings.sdk?.let { SdkInfo(project, it) }, firstDependency = settings.sdk?.let { SdkInfo(project, it) },
packageOracleFactory = ServiceManager.getService(project, IdePackageOracleFactory::class.java), packageOracleFactory = ServiceManager.getService(project, IdePackageOracleFactory::class.java),
invalidateOnOOCB = invalidateOnOOCB invalidateOnOOCB = invalidateOnOOCB,
isReleaseCoroutines = settings.isReleaseCoroutines
) )
if (delegateBuiltIns == null && builtIns is JvmBuiltIns) { if (delegateBuiltIns == null && builtIns is JvmBuiltIns) {
@@ -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.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
import org.jetbrains.kotlin.cli.common.messages.MessageCollector 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.KotlinFacetSettingsProvider
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.TargetPlatformVersion import org.jetbrains.kotlin.config.TargetPlatformVersion
@@ -41,10 +40,16 @@ import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.utils.Jsr305State import org.jetbrains.kotlin.utils.Jsr305State
object IDELanguageSettingsProvider : LanguageSettingsProvider { object IDELanguageSettingsProvider : LanguageSettingsProvider {
override fun getLanguageVersionSettings(moduleInfo: ModuleInfo, project: Project): LanguageVersionSettings = override fun getLanguageVersionSettings(
moduleInfo: ModuleInfo,
project: Project,
isReleaseCoroutines: Boolean?
): LanguageVersionSettings =
when (moduleInfo) { when (moduleInfo) {
is ModuleSourceInfo -> moduleInfo.module.languageVersionSettings 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 ScriptModuleInfo -> getVersionLanguageSettingsForScripts(project, moduleInfo.scriptDefinition)
is ScriptDependenciesInfo.ForFile -> getVersionLanguageSettingsForScripts(project, moduleInfo.scriptDefinition) is ScriptDependenciesInfo.ForFile -> getVersionLanguageSettingsForScripts(project, moduleInfo.scriptDefinition)
is PlatformModuleInfo -> moduleInfo.platformModule.module.languageVersionSettings is PlatformModuleInfo -> moduleInfo.platformModule.module.languageVersionSettings
@@ -110,13 +110,14 @@ fun Module.getStableName(): Name {
@JvmOverloads @JvmOverloads
fun Project.getLanguageVersionSettings( fun Project.getLanguageVersionSettings(
contextModule: Module? = null, 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 { ): LanguageVersionSettings {
val arguments = KotlinCommonCompilerArgumentsHolder.getInstance(this).settings val arguments = KotlinCommonCompilerArgumentsHolder.getInstance(this).settings
val languageVersion = val languageVersion =
LanguageVersion.fromVersionString(arguments.languageVersion) LanguageVersion.fromVersionString(arguments.languageVersion)
?: contextModule?.getAndCacheLanguageLevelByDependencies() ?: contextModule?.getAndCacheLanguageLevelByDependencies()
?: LanguageVersion.LATEST_STABLE ?: LanguageVersion.LATEST_STABLE
val apiVersion = ApiVersion.createByLanguageVersion(LanguageVersion.fromVersionString(arguments.apiVersion) ?: languageVersion) val apiVersion = ApiVersion.createByLanguageVersion(LanguageVersion.fromVersionString(arguments.apiVersion) ?: languageVersion)
val compilerSettings = KotlinCompilerSettings.getInstance(this).settings val compilerSettings = KotlinCompilerSettings.getInstance(this).settings
@@ -127,6 +128,12 @@ fun Project.getLanguageVersionSettings(
val extraLanguageFeatures = additionalArguments.configureLanguageFeatures(MessageCollector.NONE).apply { val extraLanguageFeatures = additionalArguments.configureLanguageFeatures(MessageCollector.NONE).apply {
configureCoroutinesSupport(CoroutineSupport.byCompilerArguments(KotlinCommonCompilerArgumentsHolder.getInstance(this@getLanguageVersionSettings).settings)) 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 { val extraAnalysisFlags = additionalArguments.configureAnalysisFlags(MessageCollector.NONE).apply {
@@ -146,7 +153,7 @@ val Module.languageVersionSettings: LanguageVersionSettings
get() { get() {
val cachedValue = val cachedValue =
getUserData(LANGUAGE_VERSION_SETTINGS) getUserData(LANGUAGE_VERSION_SETTINGS)
?: createCachedValueForLanguageVersionSettings().also { putUserData(LANGUAGE_VERSION_SETTINGS, it) } ?: createCachedValueForLanguageVersionSettings().also { putUserData(LANGUAGE_VERSION_SETTINGS, it) }
return cachedValue.value return cachedValue.value
} }
@@ -0,0 +1,4 @@
package libN
suspend fun newFoo() {}
fun newBuilder(x: suspend () -> Unit) {}
@@ -0,0 +1,4 @@
package libO
suspend fun oldFoo() {}
fun oldBuilder(x: suspend () -> Unit) {}
@@ -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)
}
}
@@ -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.ModuleInfo
import org.jetbrains.kotlin.analyzer.ResolverForModuleComputationTracker import org.jetbrains.kotlin.analyzer.ResolverForModuleComputationTracker
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments 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.config.LanguageVersion
import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo import org.jetbrains.kotlin.idea.caches.project.ModuleSourceInfo
import org.jetbrains.kotlin.idea.caches.project.SdkInfo 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.completion.test.withServiceRegistered
import org.jetbrains.kotlin.idea.facet.KotlinFacetConfiguration import org.jetbrains.kotlin.idea.facet.KotlinFacetConfiguration
import org.jetbrains.kotlin.idea.facet.KotlinFacetType import org.jetbrains.kotlin.idea.facet.KotlinFacetType
import org.jetbrains.kotlin.idea.framework.JSLibraryKind import org.jetbrains.kotlin.idea.framework.JSLibraryKind
import org.jetbrains.kotlin.idea.project.KotlinCodeBlockModificationListener import org.jetbrains.kotlin.idea.project.KotlinCodeBlockModificationListener
import org.jetbrains.kotlin.idea.project.KotlinModuleModificationTracker 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.PluginTestCaseBase
import org.jetbrains.kotlin.idea.test.allKotlinFiles import org.jetbrains.kotlin.idea.test.allKotlinFiles
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
@@ -240,6 +245,45 @@ open class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
checkHighlightingInProject() 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 { private fun Module.setupKotlinFacet(configure: KotlinFacetConfiguration.() -> Unit) = apply {
runWriteAction { runWriteAction {
val facet = FacetManager.getInstance(this).addFacet(KotlinFacetType.INSTANCE, KotlinFacetType.NAME, null) val facet = FacetManager.getInstance(this).addFacet(KotlinFacetType.INSTANCE, KotlinFacetType.NAME, null)