Support different target platforms for modules in AnalyzerFacade
Instead of reusing the same AnalyzerFacade that is used for resolution of a module to resolve its dependencies, analyze each dependency module/library with a facade depending on its target platform. Introduce and use CommonLibraryDetectionUtil in addition to KotlinJavaScriptLibraryDetectionUtil, to detect common libraries (with .kotlin_metadata files). Note that before multi-platform projects, this was not needed because there were only two platforms (JVM and JS), and JVM module had only JVM modules/libraries as dependencies, JS module had only JS modules/libraries as dependencies. Now, for example, a JVM module can have a common module/library as a dependency, and it would be incorrect to analyze that dependency with JvmAnalyzerFacade because that facade does not know anything about .kotlin_metadata files. The changes in Dsl.kt and KotlinCacheServiceImpl.kt are needed because PsiElement.getJavaDescriptorResolver, called from some IDE code, started to fail on a common module, because the container for a common module does not have the JavaDescriptorResolver
This commit is contained in:
@@ -67,7 +67,7 @@ object JsAnalyzerFacade : AnalyzerFacade<PlatformAnalysisParameters>() {
|
||||
)
|
||||
var packageFragmentProvider = container.get<ResolveSession>().packageFragmentProvider
|
||||
|
||||
if (moduleInfo is LibraryModuleInfo && moduleInfo.isJsLibrary()) {
|
||||
if (moduleInfo is LibraryModuleInfo && moduleInfo.libraryPlatform == JsPlatform) {
|
||||
val providers = moduleInfo.getLibraryRoots()
|
||||
.flatMap { KotlinJavascriptMetadataUtils.loadMetadata(it) }
|
||||
.filter { it.version.isCompatible() }
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
package org.jetbrains.kotlin.caches.resolve
|
||||
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
|
||||
interface LibraryModuleInfo : ModuleInfo {
|
||||
fun isJsLibrary(): Boolean
|
||||
val libraryPlatform: TargetPlatform
|
||||
|
||||
fun getLibraryRoots(): Collection<String>
|
||||
}
|
||||
|
||||
@@ -32,11 +32,15 @@ import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.caches.resolve.LibraryModuleInfo
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.idea.framework.CommonLibraryDetectionUtil
|
||||
import org.jetbrains.kotlin.idea.framework.KotlinJavaScriptLibraryDetectionUtil
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
||||
import org.jetbrains.kotlin.idea.util.isInSourceContentWithoutInjected
|
||||
import org.jetbrains.kotlin.js.resolve.JsPlatform
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.TargetPlatform
|
||||
import org.jetbrains.kotlin.resolve.jvm.GlobalSearchScopeWithModuleSources
|
||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||
import java.util.*
|
||||
|
||||
interface IdeaModuleInfo : ModuleInfo {
|
||||
@@ -200,7 +204,12 @@ class LibraryInfo(val project: Project, val library: Library) : IdeaModuleInfo,
|
||||
return result.toList()
|
||||
}
|
||||
|
||||
override fun isJsLibrary(): Boolean = KotlinJavaScriptLibraryDetectionUtil.isKotlinJavaScriptLibrary(library)
|
||||
override val libraryPlatform: TargetPlatform
|
||||
get() = when {
|
||||
KotlinJavaScriptLibraryDetectionUtil.isKotlinJavaScriptLibrary(library) -> JsPlatform
|
||||
CommonLibraryDetectionUtil.isCommonLibrary(library) -> TargetPlatform.Default
|
||||
else -> JvmPlatform
|
||||
}
|
||||
|
||||
override val sourcesModuleInfo: SourceForBinaryModuleInfo
|
||||
get() = LibrarySourceInfo(project, library)
|
||||
|
||||
+2
-1
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.analyzer.EmptyResolverForProject
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
|
||||
import org.jetbrains.kotlin.container.getService
|
||||
import org.jetbrains.kotlin.container.tryGetService
|
||||
import org.jetbrains.kotlin.context.GlobalContext
|
||||
import org.jetbrains.kotlin.context.GlobalContextImpl
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
@@ -164,7 +165,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
|
||||
}
|
||||
|
||||
fun <T : Any> tryGetProjectService(platform: TargetPlatform, ideaModuleInfo: IdeaModuleInfo, serviceClass: Class<T>): T? {
|
||||
return globalFacade(platform, ideaModuleInfo.sdk).tryGetResolverForModuleInfo(ideaModuleInfo)?.componentProvider?.getService(serviceClass)
|
||||
return globalFacade(platform, ideaModuleInfo.sdk).tryGetResolverForModuleInfo(ideaModuleInfo)?.componentProvider?.tryGetService(serviceClass)
|
||||
}
|
||||
|
||||
private fun globalFacade(platform: TargetPlatform, sdk: Sdk?) =
|
||||
|
||||
+14
-7
@@ -23,10 +23,12 @@ import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.JdkOrderEntry
|
||||
import com.intellij.openapi.roots.LibraryOrderEntry
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import org.jetbrains.kotlin.analyzer.AnalyzerFacade
|
||||
import org.jetbrains.kotlin.analyzer.ModuleContent
|
||||
import org.jetbrains.kotlin.analyzer.ModuleInfo
|
||||
import org.jetbrains.kotlin.analyzer.ResolverForProject
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.caches.resolve.LibraryModuleInfo
|
||||
import org.jetbrains.kotlin.context.GlobalContextImpl
|
||||
import org.jetbrains.kotlin.context.withProject
|
||||
import org.jetbrains.kotlin.idea.project.AnalyzerFacadeProvider
|
||||
@@ -78,15 +80,13 @@ fun createModuleResolverProvider(
|
||||
psiClass.getNullableModuleInfo()
|
||||
}
|
||||
|
||||
return AnalyzerFacadeProvider.getAnalyzerFacade(platform).setupResolverForProject(
|
||||
debugName, globalContext.withProject(project), modulesToCreateResolversFor, modulesContent,
|
||||
jvmPlatformParameters, IdeaEnvironment, builtInsProvider,
|
||||
return AnalyzerFacade.setupResolverForProject(
|
||||
debugName, globalContext.withProject(project), modulesToCreateResolversFor,
|
||||
{ module -> AnalyzerFacadeProvider.getAnalyzerFacade(module.getTargetPlatform() ?: platform) },
|
||||
modulesContent, jvmPlatformParameters, IdeaEnvironment, builtInsProvider,
|
||||
delegateResolver, { _, c -> IDEPackagePartProvider(c.moduleContentScope) },
|
||||
sdk?.let { SdkInfo(project, it) },
|
||||
modulePlatforms = { moduleInfo ->
|
||||
val module = (moduleInfo as? ModuleSourceInfo)?.module
|
||||
module?.let { TargetPlatformDetector.getPlatform(module).multiTargetPlatform }
|
||||
}
|
||||
modulePlatforms = { module -> module.getTargetPlatform()?.multiTargetPlatform }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -105,6 +105,13 @@ fun createModuleResolverProvider(
|
||||
)
|
||||
}
|
||||
|
||||
private fun ModuleInfo.getTargetPlatform(): TargetPlatform? =
|
||||
when (this) {
|
||||
is ModuleSourceInfo -> TargetPlatformDetector.getPlatform(module)
|
||||
is LibraryModuleInfo -> libraryPlatform
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun collectAllModuleInfosFromIdeaModel(project: Project): List<IdeaModuleInfo> {
|
||||
val ideaModules = ModuleManager.getInstance(project).modules.toList()
|
||||
val modulesSourcesInfos = ideaModules.flatMap { listOf(it.productionSourceInfo(), it.testSourceInfo()) }
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.framework
|
||||
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.idea.caches.JarUserDataManager
|
||||
import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment
|
||||
|
||||
object CommonLibraryDetectionUtil {
|
||||
fun isCommonLibrary(library: Library): Boolean {
|
||||
if (library is LibraryEx && library.isDisposed) return false
|
||||
|
||||
for (root in library.getFiles(OrderRootType.CLASSES)) {
|
||||
val hasMetadata = JarUserDataManager.hasFileWithProperty(HasCommonKotlinMetadataInJar, root)
|
||||
if (hasMetadata != null) {
|
||||
return hasMetadata
|
||||
}
|
||||
|
||||
if (!VfsUtilCore.processFilesRecursively(root) { !isKotlinMetadataFile(it) }) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun isKotlinMetadataFile(file: VirtualFile): Boolean =
|
||||
!file.isDirectory &&
|
||||
file.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION
|
||||
|
||||
object HasCommonKotlinMetadataInJar : JarUserDataManager.JarBooleanPropertyCounter(HasCommonKotlinMetadataInJar::class.simpleName!!) {
|
||||
override fun hasProperty(file: VirtualFile) = isKotlinMetadataFile(file)
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.caches.JarUserDataManager;
|
||||
import org.jetbrains.kotlin.idea.debugger.filter.DebuggerFiltersUtilKt;
|
||||
import org.jetbrains.kotlin.idea.framework.CommonLibraryDetectionUtil;
|
||||
import org.jetbrains.kotlin.idea.framework.KotlinJavaScriptLibraryDetectionUtil;
|
||||
import org.jetbrains.kotlin.utils.PathUtil;
|
||||
|
||||
@@ -59,6 +60,7 @@ public class PluginStartupComponent implements ApplicationComponent {
|
||||
}
|
||||
|
||||
JarUserDataManager.INSTANCE.register(KotlinJavaScriptLibraryDetectionUtil.HasKotlinJSMetadataInJar.INSTANCE);
|
||||
JarUserDataManager.INSTANCE.register(CommonLibraryDetectionUtil.HasCommonKotlinMetadataInJar.INSTANCE);
|
||||
|
||||
DebuggerFiltersUtilKt.addKotlinStdlibDebugFilterIfNeeded();
|
||||
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package test
|
||||
|
||||
class MyException : Exception()
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
package test
|
||||
|
||||
fun test(f: () -> Unit) {
|
||||
try {
|
||||
f()
|
||||
}
|
||||
catch (e: MyException) {
|
||||
f()
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.idea.caches.resolve
|
||||
|
||||
import com.intellij.openapi.roots.DependencyScope
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.config.TargetPlatformKind
|
||||
|
||||
@@ -64,9 +65,12 @@ class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
|
||||
class MultiPlatform : AbstractMultiModuleHighlightingTest() {
|
||||
override val testPath get() = super.testPath + "multiplatform/"
|
||||
|
||||
private fun doTest(vararg platforms: TargetPlatformKind<*>) {
|
||||
private fun doTest(vararg platforms: TargetPlatformKind<*>, withStdlibCommon: Boolean = false) {
|
||||
val commonModule = module("common")
|
||||
commonModule.createFacet(TargetPlatformKind.Common)
|
||||
if (withStdlibCommon) {
|
||||
commonModule.addLibrary(ForTestCompileRuntime.stdlibCommonForTests())
|
||||
}
|
||||
|
||||
for (platform in platforms) {
|
||||
val path = when (platform) {
|
||||
@@ -106,5 +110,9 @@ class MultiModuleHighlightingTest : AbstractMultiModuleHighlightingTest() {
|
||||
fun testSuppressHeaderWithoutImpl() {
|
||||
doTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6])
|
||||
}
|
||||
|
||||
fun testCatchHeaderExceptionInPlatformModule() {
|
||||
doTest(TargetPlatformKind.Jvm[JvmTarget.JVM_1_6], withStdlibCommon = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,11 @@ import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.StdModuleTypes
|
||||
import com.intellij.openapi.roots.DependencyScope
|
||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.testFramework.PsiTestUtil
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||
import org.jetbrains.kotlin.cli.common.parser.com.sampullara.cli.Argument
|
||||
@@ -35,6 +38,7 @@ import org.jetbrains.kotlin.config.TargetPlatformKind
|
||||
import org.jetbrains.kotlin.idea.facet.getOrCreateFacet
|
||||
import org.jetbrains.kotlin.idea.facet.initializeIfNeeded
|
||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||
import org.jetbrains.kotlin.idea.test.KotlinJdkAndLibraryProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import java.io.File
|
||||
|
||||
@@ -75,6 +79,13 @@ abstract class AbstractMultiModuleTest : DaemonAnalyzerTestCase() {
|
||||
exported: Boolean = false
|
||||
) = ModuleRootModificationUtil.addDependency(this, other, dependencyScope, exported)
|
||||
|
||||
protected fun Module.addLibrary(jar: File) {
|
||||
ConfigLibraryUtil.addLibrary(NewLibraryEditor().apply {
|
||||
name = KotlinJdkAndLibraryProjectDescriptor.LIBRARY_NAME
|
||||
addRoot(VfsUtil.getUrlForLibraryRoot(jar), OrderRootType.CLASSES)
|
||||
}, this)
|
||||
}
|
||||
|
||||
protected fun Module.createFacet(platformKind: TargetPlatformKind<*>? = null) {
|
||||
val accessToken = WriteAction.start()
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user