From f72aa78eecc4251106dbb2ccac888139740cc639 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Thu, 19 Jul 2018 16:18:46 +0300 Subject: [PATCH] Fix libraries analysis for case of isReleaseCoroutines feature enabled #KT-25466 In Progress --- .../kotlin/analyzer/AnalyzerFacade.kt | 18 ++++++-- .../analyzer/common/CommonAnalyzerFacade.kt | 7 ++- .../caches/resolve/KotlinCacheServiceImpl.kt | 29 +++++++++--- .../caches/resolve/ProjectResolutionFacade.kt | 3 +- .../compiler/IDELanguageSettingsProvider.kt | 11 +++-- .../jetbrains/kotlin/idea/project/Platform.kt | 15 +++++-- .../libNew/libN.kt | 4 ++ .../libOld/libO.kt | 4 ++ .../moduleNew/main.kt | 34 ++++++++++++++ .../moduleOld/main.kt | 17 +++++++ .../resolve/MultiModuleHighlightingTest.kt | 44 +++++++++++++++++++ 11 files changed, 168 insertions(+), 18 deletions(-) create mode 100644 idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/libNew/libN.kt create mode 100644 idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/libOld/libO.kt create mode 100644 idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/moduleNew/main.kt create mode 100644 idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/moduleOld/main.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt index 282b82d1b16..5b9d5a08720 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/AnalyzerFacade.kt @@ -94,7 +94,8 @@ class ResolverForProjectImpl( private val delegateResolver: ResolverForProject = 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() { private class ModuleData( @@ -176,7 +177,8 @@ class ResolverForProjectImpl( 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 } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/common/CommonAnalyzerFacade.kt b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/common/CommonAnalyzerFacade.kt index 00c1e464c29..17781c77eb5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/analyzer/common/CommonAnalyzerFacade.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/analyzer/common/CommonAnalyzerFacade.kt @@ -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 }, diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheServiceImpl.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheServiceImpl.kt index 7ddab78704d..5311c99d9d6 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheServiceImpl.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheServiceImpl.kt @@ -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): 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 diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ProjectResolutionFacade.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ProjectResolutionFacade.kt index ba442d5ac08..baa9bafdc53 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ProjectResolutionFacade.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/ProjectResolutionFacade.kt @@ -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) { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IDELanguageSettingsProvider.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IDELanguageSettingsProvider.kt index c8c8f7d354b..9e18d7fc586 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IDELanguageSettingsProvider.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IDELanguageSettingsProvider.kt @@ -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 diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt index b79848fe91f..cb0e61495a8 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt @@ -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 } diff --git a/idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/libNew/libN.kt b/idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/libNew/libN.kt new file mode 100644 index 00000000000..cfbd78ea817 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/libNew/libN.kt @@ -0,0 +1,4 @@ +package libN + +suspend fun newFoo() {} +fun newBuilder(x: suspend () -> Unit) {} diff --git a/idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/libOld/libO.kt b/idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/libOld/libO.kt new file mode 100644 index 00000000000..4e1a2e6f5c1 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/libOld/libO.kt @@ -0,0 +1,4 @@ +package libO + +suspend fun oldFoo() {} +fun oldBuilder(x: suspend () -> Unit) {} diff --git a/idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/moduleNew/main.kt b/idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/moduleNew/main.kt new file mode 100644 index 00000000000..f9d3ad8a766 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/moduleNew/main.kt @@ -0,0 +1,34 @@ +import libN.* +import libO.* + +suspend fun newMain() { + newFoo() + oldFoo() + + oldFoo( + object : kotlin.coroutines.experimental.Continuation { + 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 { + newMain() + + // `suspend () -> Unit` becomes (Continuation -> Any?) + it.resume(Unit) + } +} diff --git a/idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/moduleOld/main.kt b/idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/moduleOld/main.kt new file mode 100644 index 00000000000..13efdd5b011 --- /dev/null +++ b/idea/testData/multiModuleHighlighting/coroutineMixedReleaseStatus/moduleOld/main.kt @@ -0,0 +1,17 @@ +import libN.* +import libO.* + +suspend fun oldMain() { + newFoo() + oldFoo() +} + +fun oldMain2() { + newBuilder { + oldMain() + } + + oldBuilder { + oldMain() + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt index 95647a8c3a4..7507aa47c2a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/MultiModuleHighlightingTest.kt @@ -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)