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 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 },
@@ -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
@@ -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) {
@@ -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
}
@@ -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.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)