Implement modules in IDE

IDE:
Rewrite AnalyzerFacade and implementations for JS and JVM to support creating separate analyzers for each module
Introduce ModuleInfo which is an intermediate entity between configuration (tests or idea modules) and ModuleDescriptor
Implement IdeaModuleInfos which represent IDEA modules, sdks and libraries
Add (somewhat thin) test checking their behaviour
Implement getModuleInfo() - utility to obtain IdeaModuleInfo for PsiElement
Drop Project.getLazyResolveSession() - not possible to obtain resolve session for the whole project any more
Adjust JavaResolveExtension accordingly
KotlinSignature Intention/Marker - make sure that analyzed element is cls element (he's not in resolve scope otherwise)

LightClasses:
Create separate package light classes for each module
Java code can only reference light class from the first module among it's dependencies
Duplicate jvm signature is only reported on package declarations inside one module

Injectors:
Receive GlobalSearchScope as paramer for VirtualFileFinder and JavaClassFinder
which allows to narrow analyzer scope

JDR:
Introduce ModuleClassResolver resolves java classes in correct java descriptor resolver (corresponding ModuleDescriptor)
Add test checking that java classes belong to correct module

Debugger:
Provide context to analyze files created by debugger in

Converter:
Postprocessor now needs a context to analyze resulting code in

JetPsiFactory:
Add verification that files created by psi factory are not analyzed without context (that is almost never a good idea)

Other:
Use new API in various tests, utilities, run configuration producers and builtin serializers
Various "TODO: (module refactoring)" which mark the unfinished parts
This commit is contained in:
Pavel V. Talanov
2014-06-10 16:50:35 +04:00
parent 07935c837a
commit db5303c019
82 changed files with 1813 additions and 527 deletions
@@ -24,6 +24,8 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.containers.MultiMap;
import kotlin.Function1;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.asJava.KotlinLightClassForExplicitDeclaration;
@@ -65,20 +67,10 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
public IDELightClassGenerationSupport(@NotNull Project project) {
this.project = project;
final GlobalSearchScope searchScope = GlobalSearchScope.allScope(project);
this.jetFileComparator = new Comparator<JetFile>() {
@Override
public int compare(@NotNull JetFile o1, @NotNull JetFile o2) {
VirtualFile f1 = o1.getVirtualFile();
VirtualFile f2 = o2.getVirtualFile();
if (f1 == f2) return 0;
if (f1 == null) return -1;
if (f2 == null) return 1;
return searchScope.compare(f1, f2);
}
};
this.jetFileComparator = byScopeComparator(GlobalSearchScope.allScope(project));
}
@NotNull
@Override
public LightClassConstructionContext getContextForPackage(@NotNull Collection<JetFile> files) {
@@ -169,6 +161,47 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
return PackageIndexUtil.findFilesWithExactPackage(fqName, kotlinSources(searchScope, project), project);
}
@NotNull
@Override
public List<KotlinLightPackageClassInfo> findPackageClassesInfos(
@NotNull FqName fqName, @NotNull GlobalSearchScope wholeScope
) {
Collection<JetFile> allFiles = findFilesForPackage(fqName, wholeScope);
Map<IdeaModuleInfo, List<JetFile>> filesByInfo = groupByModuleInfo(allFiles);
List<KotlinLightPackageClassInfo> result = new ArrayList<KotlinLightPackageClassInfo>();
for (Map.Entry<IdeaModuleInfo, List<JetFile>> entry : filesByInfo.entrySet()) {
result.add(new KotlinLightPackageClassInfo(entry.getValue(), entry.getKey().contentScope()));
}
sortByClasspath(wholeScope, result);
return result;
}
@NotNull
private static Map<IdeaModuleInfo, List<JetFile>> groupByModuleInfo(@NotNull Collection<JetFile> allFiles) {
return KotlinPackage.groupByTo(
allFiles,
new LinkedHashMap<IdeaModuleInfo, List<JetFile>>(),
new Function1<JetFile, IdeaModuleInfo>() {
@Override
public IdeaModuleInfo invoke(JetFile file) {
return ResolvePackage.getModuleInfo(file);
}
});
}
private static void sortByClasspath(@NotNull GlobalSearchScope wholeScope, @NotNull List<KotlinLightPackageClassInfo> result) {
final Comparator<JetFile> byScopeComparator = byScopeComparator(wholeScope);
Collections.sort(result, new Comparator<KotlinLightPackageClassInfo>() {
@Override
public int compare(@NotNull KotlinLightPackageClassInfo info1, @NotNull KotlinLightPackageClassInfo info2) {
JetFile file1 = info1.getFiles().iterator().next();
JetFile file2 = info2.getFiles().iterator().next();
//classes earlier that would appear earlier on classpath should go first
return -byScopeComparator.compare(file1, file2);
}
});
}
@NotNull
@Override
public Collection<JetClassOrObject> findClassOrObjectDeclarationsInPackage(
@@ -211,4 +244,19 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
return result;
}
@NotNull
private static Comparator<JetFile> byScopeComparator(@NotNull final GlobalSearchScope searchScope) {
return new Comparator<JetFile>() {
@Override
public int compare(@NotNull JetFile o1, @NotNull JetFile o2) {
VirtualFile f1 = o1.getVirtualFile();
VirtualFile f2 = o2.getVirtualFile();
if (f1 == f2) return 0;
if (f1 == null) return -1;
if (f2 == null) return 1;
return searchScope.compare(f1, f2);
}
};
}
}
@@ -0,0 +1,134 @@
/*
* Copyright 2010-2014 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.jet.plugin.caches.resolve
import org.jetbrains.jet.analyzer.ModuleInfo
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.OrderEntry
import com.intellij.openapi.roots.ModuleSourceOrderEntry
import com.intellij.openapi.roots.ModuleOrderEntry
import com.intellij.openapi.roots.LibraryOrderEntry
import com.intellij.openapi.roots.JdkOrderEntry
import com.intellij.openapi.module.Module
import org.jetbrains.jet.lang.resolve.name.Name
import java.util.LinkedHashSet
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.module.impl.scopes.LibraryScopeBase
import com.intellij.openapi.roots.OrderRootType
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.jet.utils.emptyOrSingletonList
private abstract class IdeaModuleInfo : ModuleInfo {
abstract fun contentScope(): GlobalSearchScope
}
private fun orderEntryToModuleInfo(project: Project, orderEntry: OrderEntry): List<IdeaModuleInfo> {
return when (orderEntry) {
is ModuleSourceOrderEntry -> {
listOf(orderEntry.getOwnerModule().toSourceInfo())
}
is ModuleOrderEntry -> {
emptyOrSingletonList(orderEntry.getModule()?.toSourceInfo())
}
is LibraryOrderEntry -> {
val library = orderEntry.getLibrary() ?: return listOf()
emptyOrSingletonList(LibraryInfo(project, library))
}
is JdkOrderEntry -> {
val sdk = orderEntry.getJdk() ?: return listOf()
emptyOrSingletonList(SdkInfo(project, sdk))
}
else -> {
throw IllegalStateException("Unexpected order entry $orderEntry")
}
}
}
//TODO: (module refactoring) there should be separate ModuleTestInfo
private data class ModuleSourceInfo(val module: Module) : IdeaModuleInfo() {
override val name = Name.special("<sources for module ${module.getName()}>")
override fun contentScope() = GlobalSearchScope.moduleScope(module)
override fun dependencies(): List<IdeaModuleInfo> {
//NOTE: lib dependencies can be processed several times during recursive traversal
val result = LinkedHashSet<IdeaModuleInfo>()
ModuleRootManager.getInstance(module).orderEntries().compileOnly().recursively().exportedOnly().forEach {
orderEntry ->
result.addAll(orderEntryToModuleInfo(module.getProject(), orderEntry!!))
true
}
return result.toList()
}
}
private fun Module.toSourceInfo() = ModuleSourceInfo(this)
private data class LibraryInfo(val project: Project, val library: Library) : IdeaModuleInfo() {
override val name: Name = Name.special("<library ${library.getName()}>")
override fun contentScope() = LibraryWithoutSourceScope(project, library)
override fun dependencies(): List<IdeaModuleInfo> {
//TODO: (module refactoring) heuristic dependencies for libraries
val orderEntry = ModuleManager.getInstance(project).getModules().stream().flatMap {
ModuleRootManager.getInstance(it).getOrderEntries().stream()
}.firstOrNull { it is JdkOrderEntry } as? JdkOrderEntry
val sdk = orderEntry?.getJdk()
return if (sdk != null) listOf(SdkInfo(project, sdk), this) else listOf(this)
}
}
private data class LibrarySourceInfo(val project: Project, val library: Library) : IdeaModuleInfo() {
override val name: Name = Name.special("<sources for library ${library.getName()}>")
override fun contentScope() = GlobalSearchScope.EMPTY_SCOPE
override fun dependencies(): List<IdeaModuleInfo> {
return listOf(this) + LibraryInfo(project, library).dependencies()
}
}
//TODO: (module refactoring) there should be separate SdkSourceInfo but there are no kotlin source in existing sdks for now :)
private data class SdkInfo(val project: Project, val sdk: Sdk) : IdeaModuleInfo() {
override val name: Name = Name.special("<library ${sdk.getName()}>")
override fun contentScope() = SdkScope(project, sdk)
override fun dependencies(): List<IdeaModuleInfo> = listOf(this)
}
private object NotUnderContentRootModuleInfo : IdeaModuleInfo() {
override val name: Name = Name.special("<special module for files not under source root>")
override fun contentScope() = GlobalSearchScope.EMPTY_SCOPE
//TODO: (module refactoring) dependency on runtime can be of use here
override fun dependencies(): List<IdeaModuleInfo> = listOf(this)
}
private data class LibraryWithoutSourceScope(project: Project, private val library: Library) :
LibraryScopeBase(project, library.getFiles(OrderRootType.CLASSES), array<VirtualFile>()) {
}
//TODO: (module refactoring) android sdk has modified scope
private data class SdkScope(project: Project, private val sdk: Sdk) :
LibraryScopeBase(project, sdk.getRootProvider().getFiles(OrderRootType.CLASSES), array<VirtualFile>())
@@ -0,0 +1,41 @@
/*
* Copyright 2010-2014 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.jet.plugin.caches.resolve
import org.jetbrains.jet.plugin.project.TargetPlatform
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.resolve.java.JvmResolverForModule
import com.intellij.openapi.project.Project
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver
import org.jetbrains.jet.lang.resolve.BindingContext
public object JavaResolveExtension : CacheExtension<(PsiElement) -> Pair<JavaDescriptorResolver, BindingContext>> {
override val platform: TargetPlatform = TargetPlatform.JVM
override fun getData(resolverProvider: ModuleResolverProvider): (PsiElement) -> Pair<JavaDescriptorResolver, BindingContext> {
return {
val resolverForModule = resolverProvider.resolverByModule(it.getModuleInfo()) as JvmResolverForModule
Pair(resolverForModule.javaDescriptorResolver, resolverForModule.lazyResolveSession.getBindingContext())
}
}
public fun getResolver(project: Project, element: PsiElement): JavaDescriptorResolver =
KotlinCacheService.getInstance(project)[this](element).first
public fun getContext(project: Project, element: PsiElement): BindingContext =
KotlinCacheService.getInstance(project)[this](element).second
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2014 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.jet.plugin.caches.resolve
import org.jetbrains.jet.lang.resolve.lazy.ResolveSession
import org.jetbrains.jet.analyzer.ResolverForModule
import org.jetbrains.jet.lang.psi.JetFile
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.jet.analyzer.PlatformAnalysisParameters
import org.jetbrains.jet.analyzer.AnalyzerFacade
import com.intellij.openapi.project.Project
import org.jetbrains.jet.context.GlobalContext
import org.jetbrains.jet.analyzer.ResolverForProject
import org.jetbrains.k2js.analyze.AnalyzerFacadeForJS
import org.jetbrains.jet.lang.PlatformToKotlinClassMap
import org.jetbrains.jet.lang.resolve.lazy.declarations.DeclarationProviderFactoryService
import org.jetbrains.jet.lang.resolve.BindingTraceContext
import org.jetbrains.jet.di.InjectorForLazyResolve
import org.jetbrains.jet.lang.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.jet.analyzer.ModuleInfo
import org.jetbrains.jet.analyzer.ModuleContent
public class JsResolverForModule(
override val lazyResolveSession: ResolveSession
) : ResolverForModule
public object JsAnalyzerFacade : AnalyzerFacade<JsResolverForModule, PlatformAnalysisParameters> {
override fun <M : ModuleInfo> createResolverForModule(
project: Project,
globalContext: GlobalContext,
moduleDescriptor: ModuleDescriptorImpl,
moduleContent: ModuleContent,
platformParameters: PlatformAnalysisParameters,
resolverForProject: ResolverForProject<M, JsResolverForModule>
): JsResolverForModule {
val (syntheticFiles, moduleContentScope) = moduleContent
val declarationProviderFactory = DeclarationProviderFactoryService.createDeclarationProviderFactory(
project, globalContext.storageManager, syntheticFiles, moduleContentScope
)
val injector = InjectorForLazyResolve(project, globalContext, moduleDescriptor, declarationProviderFactory, BindingTraceContext())
val resolveSession = injector.getResolveSession()!!
moduleDescriptor.initialize(resolveSession.getPackageFragmentProvider())
return JsResolverForModule(resolveSession)
}
override val defaultImports = AnalyzerFacadeForJS.DEFAULT_IMPORTS
override val platformToKotlinClassMap = PlatformToKotlinClassMap.EMPTY
}
@@ -33,7 +33,12 @@ import org.jetbrains.jet.plugin.project.TargetPlatform.*
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
import org.jetbrains.jet.plugin.project.TargetPlatformDetector
import org.jetbrains.jet.lang.psi.JetCodeFragment
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.descriptors.ModuleDescriptor
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.jet.context.GlobalContext
import org.jetbrains.jet.plugin.stubindex.JetSourceFilterScope
import com.intellij.psi.PsiElement
private val LOG = Logger.getInstance(javaClass<KotlinCacheService>())
@@ -41,10 +46,6 @@ public fun JetElement.getLazyResolveSession(): ResolveSessionForBodies {
return KotlinCacheService.getInstance(getProject()).getLazyResolveSession(this)
}
public fun Project.getLazyResolveSession(platform: TargetPlatform): ResolveSessionForBodies {
return KotlinCacheService.getInstance(this).getGlobalLazyResolveSession(platform)
}
public fun JetElement.getAnalysisResults(): AnalyzeExhaust {
return KotlinCacheService.getInstance(getProject()).getAnalysisResults(listOf(this))
}
@@ -64,19 +65,16 @@ public class KotlinCacheService(val project: Project) {
public fun getInstance(project: Project): KotlinCacheService = ServiceManager.getService(project, javaClass<KotlinCacheService>())!!
}
private fun globalResolveSessionProvider(platform: TargetPlatform, syntheticFiles: Collection<JetFile> = listOf()) = {
val setup = AnalyzerFacadeProvider.getAnalyzerFacade(platform)
.createSetup(project, syntheticFiles, GlobalSearchScope.allScope(project))
val resolveSessionForBodies = ResolveSessionForBodies(project, setup.getLazyResolveSession())
private fun globalResolveSessionProvider(platform: TargetPlatform, syntheticFiles: Collection<JetFile> = listOf()):
() -> CachedValueProvider.Result<ModuleResolverProvider> = {
val analyzerFacade = AnalyzerFacadeProvider.getAnalyzerFacade(platform)
val moduleMapping = createModuleResolverProvider(project, analyzerFacade, syntheticFiles)
CachedValueProvider.Result.create(
SessionAndSetup(
platform,
resolveSessionForBodies,
setup
),
moduleMapping,
PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT,
resolveSessionForBodies
moduleMapping.exceptionTracker
)
}
private val globalCachesPerPlatform = mapOf(
@@ -102,17 +100,17 @@ public class KotlinCacheService(val project: Project) {
}
}
public fun getGlobalLazyResolveSession(platform: TargetPlatform): ResolveSessionForBodies {
return globalCachesPerPlatform[platform]!!.getLazyResolveSession()
public fun getGlobalLazyResolveSession(file: JetFile, platform: TargetPlatform): ResolveSessionForBodies {
return globalCachesPerPlatform[platform]!!.getLazyResolveSession(file)
}
public fun getLazyResolveSession(element: JetElement): ResolveSessionForBodies {
val file = element.getContainingJetFile()
if (!isFileInScope(file)) {
return getCacheForSyntheticFile(file).getLazyResolveSession()
return getCacheForSyntheticFile(file).getLazyResolveSession(file)
}
return getGlobalLazyResolveSession(TargetPlatformDetector.getPlatform(file))
return getGlobalLazyResolveSession(file, TargetPlatformDetector.getPlatform(file))
}
public fun getAnalysisResults(elements: Collection<JetElement>): AnalyzeExhaust {
@@ -38,7 +38,6 @@ import com.intellij.openapi.roots.libraries.LibraryUtil
import org.jetbrains.jet.lang.resolve.LibrarySourceHacks
import org.jetbrains.jet.plugin.project.TargetPlatform
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
import org.jetbrains.jet.analyzer.AnalyzerFacade
import java.util.HashMap
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.resolve.BindingContext
@@ -64,43 +63,37 @@ import org.jetbrains.jet.analyzer.analyzeInContext
import org.jetbrains.jet.lang.resolve.BindingTraceContext
import org.jetbrains.jet.lang.types.TypeUtils
import org.jetbrains.jet.lang.resolve.scopes.ChainedScope
import org.jetbrains.jet.lang.descriptors.ModuleDescriptor
public trait CacheExtension<T> {
public val platform: TargetPlatform
public fun getData(setup: AnalyzerFacade.Setup): T
public fun getData(resolverProvider: ModuleResolverProvider): T
}
private class SessionAndSetup(
val platform: TargetPlatform,
val resolveSessionForBodies: ResolveSessionForBodies,
val setup: AnalyzerFacade.Setup
)
private class KotlinResolveCache(
val project: Project,
setupProvider: () -> CachedValueProvider.Result<SessionAndSetup>
setupProvider: () -> CachedValueProvider.Result<ModuleResolverProvider>
) {
private val setupCache = SynchronizedCachedValue(project, setupProvider, trackValue = false)
private val resolverCache = SynchronizedCachedValue(project, setupProvider, trackValue = false)
public fun getLazyResolveSession(): ResolveSessionForBodies = setupCache.getValue().resolveSessionForBodies
public fun <T> get(extension: CacheExtension<T>): T {
val sessionAndSetup = setupCache.getValue()
assert(extension.platform == sessionAndSetup.platform,
"Extension $extension declares platfrom ${extension.platform} which is incompatible with ${sessionAndSetup.platform}")
return extension.getData(sessionAndSetup.setup)
public fun getLazyResolveSession(element: JetElement): ResolveSessionForBodies {
return resolverCache.getValue().resolveSessionForBodiesByModule(element.getModuleInfo())
}
private val analysisResults = CachedValuesManager.getManager(project).createCachedValue ({
val resolveSession = getLazyResolveSession()
public fun <T> get(extension: CacheExtension<T>): T {
return extension.getData(resolverCache.getValue())
}
private val analysisResults = CachedValuesManager.getManager(project).createCachedValue(
{
val resolverProvider = resolverCache.getValue()
val results = object : SLRUCache<JetFile, PerFileAnalysisCache>(2, 3) {
override fun createValue(file: JetFile?): PerFileAnalysisCache {
return PerFileAnalysisCache(file!!, resolveSession)
return PerFileAnalysisCache(file!!, resolverProvider.resolveSessionForBodiesByModule(file.getModuleInfo()))
}
}
CachedValueProvider.Result(results, PsiModificationTracker.MODIFICATION_COUNT, resolveSession.getExceptionTracker())
CachedValueProvider.Result(results, PsiModificationTracker.MODIFICATION_COUNT, resolverProvider.exceptionTracker)
}, false)
fun getAnalysisResultsForElements(elements: Collection<JetElement>): AnalyzeExhaust {
@@ -118,7 +111,8 @@ private class KotlinResolveCache(
return if (error != null)
AnalyzeExhaust.error(bindingContext, error.getError())
else
AnalyzeExhaust.success(bindingContext, getLazyResolveSession().getModuleDescriptor())
//TODO: (module refactoring) several elements are passed here in debugger
AnalyzeExhaust.success(bindingContext, getLazyResolveSession(elements.first()).getModuleDescriptor())
}
}
@@ -0,0 +1,119 @@
/*
* Copyright 2010-2014 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.jet.plugin.caches.resolve
import com.intellij.openapi.project.Project
import org.jetbrains.jet.context.GlobalContext
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.module.ModuleManager
import org.jetbrains.jet.utils.keysToMap
import com.intellij.openapi.roots.LibraryOrderEntry
import org.jetbrains.jet.plugin.project.ResolveSessionForBodies
import org.jetbrains.jet.lang.resolve.java.JvmPlatformParameters
import org.jetbrains.jet.lang.resolve.java.structure.impl.JavaClassImpl
import com.intellij.openapi.roots.JdkOrderEntry
import org.jetbrains.kotlin.util.sure
import org.jetbrains.jet.analyzer.AnalyzerFacade
import org.jetbrains.jet.analyzer.ResolverForModule
import org.jetbrains.jet.lang.psi.*
import org.jetbrains.jet.storage.ExceptionTracker
import org.jetbrains.jet.lang.resolve.java.structure.JavaClass
import org.jetbrains.jet.analyzer.ResolverForProject
import org.jetbrains.jet.analyzer.ModuleContent
import org.jetbrains.jet.analyzer.PlatformAnalysisParameters
import org.jetbrains.jet.plugin.caches.resolve
fun createModuleResolverProvider(
project: Project,
analyzerFacade: AnalyzerFacade<ResolverForModule, JvmPlatformParameters>,
syntheticFiles: Collection<JetFile>
): ModuleResolverProvider {
val allModuleInfos = collectAllModuleInfosFromIdeaModel(project).toHashSet()
val globalContext = GlobalContext()
fun createResolverForProject(): ResolverForProject<IdeaModuleInfo, ResolverForModule> {
val syntheticFilesByModule = syntheticFiles.groupBy { it.getModuleInfo() }
allModuleInfos.addAll(syntheticFilesByModule.keySet())
val modulesContent = {(module: IdeaModuleInfo) ->
ModuleContent(syntheticFilesByModule[module] ?: listOf(), module.contentScope())
}
val jvmPlatformParameters = JvmPlatformParameters {
(javaClass: JavaClass) ->
val psiClass = (javaClass as JavaClassImpl).getPsi()
psiClass.getModuleInfo()
}
val resolverForProject = analyzerFacade.setupResolverForProject(
globalContext, project, allModuleInfos, modulesContent, jvmPlatformParameters
)
return resolverForProject
}
val resolverForProject = createResolverForProject()
val moduleToBodiesResolveSession = allModuleInfos.keysToMap {
module ->
val analyzer = resolverForProject.resolverForModule(module)
ResolveSessionForBodies(project, analyzer.lazyResolveSession)
}
return ModuleResolverProvider(
resolverForProject,
moduleToBodiesResolveSession,
globalContext.exceptionTracker
)
}
private fun collectAllModuleInfosFromIdeaModel(project: Project): List<IdeaModuleInfo> {
val ideaModules = ModuleManager.getInstance(project).getModules().toList()
val modulesSourcesInfos = ideaModules.map { it.toSourceInfo() }
//TODO: (module refactoring) include libraries that are not among dependencies of any module
val ideaLibraries = ideaModules.flatMap {
ModuleRootManager.getInstance(it).getOrderEntries().filterIsInstance(javaClass<LibraryOrderEntry>()).map {
it.getLibrary()
}
}.filterNotNull().toSet()
val librariesInfos = ideaLibraries.map { LibraryInfo(project, it) }
val ideaSdks = ideaModules.flatMap {
ModuleRootManager.getInstance(it).getOrderEntries().filterIsInstance(javaClass<JdkOrderEntry>()).map {
it.getJdk()
}
}.filterNotNull().toSet()
val sdksInfos = ideaSdks.map { SdkInfo(project, it) }
val collectAllModuleInfos = modulesSourcesInfos + librariesInfos + sdksInfos
return collectAllModuleInfos
}
class ModuleResolverProvider(
private val resolverForProject: ResolverForProject<IdeaModuleInfo, *>,
private val bodiesResolveByModule: Map<IdeaModuleInfo, ResolveSessionForBodies>,
val exceptionTracker: ExceptionTracker
) {
fun resolverByModule(module: IdeaModuleInfo): ResolverForModule = resolverForProject.resolverForModule(module)
fun resolveSessionForBodiesByModule(module: IdeaModuleInfo) =
//NOTE: if this assert fails in production, additional information can be obtained by logging on the call site
bodiesResolveByModule[module] ?: throw AssertionError("Requested data for $module not contained in this resolver.")
}
@@ -0,0 +1,101 @@
/*
* Copyright 2010-2014 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.jet.plugin.caches.resolve
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.resolve.java.jetAsJava.KotlinLightElement
import org.jetbrains.jet.lang.psi.*
import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.roots.LibraryOrSdkOrderEntry
import com.intellij.openapi.roots.LibraryOrderEntry
import com.intellij.openapi.roots.JdkOrderEntry
import org.jetbrains.jet.asJava.FakeLightClassForFileOfPackage
import org.jetbrains.jet.asJava.KotlinLightClassForPackage
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
fun PsiElement.getModuleInfo(): IdeaModuleInfo {
fun logAndReturnDefault(message: String): IdeaModuleInfo {
LOG.error("Could not find correct module information.\nReason: $message")
return NotUnderContentRootModuleInfo
}
if (this is KotlinLightElement<*, *>)
return this.getModuleInfoForLightElement()
if (this is JetCodeFragment)
return this.getContext()?.getModuleInfo()
?: logAndReturnDefault("Analyzing code fragment of type $javaClass with no context element\nText:\n${getText()}")
val containingJetFile = (this as? JetElement)?.getContainingFile() as? JetFile
val context = containingJetFile?.analysisContext
if (context != null) return context.getModuleInfo()
val doNotAnalyze = containingJetFile?.doNotAnalyze
if (doNotAnalyze != null) {
return logAndReturnDefault(
"Should not analyze element: ${getText()} in file ${containingJetFile?.getName() ?: " <no file>"}\n$doNotAnalyze"
)
}
val project = getProject()
val containingFile = getContainingFile()
?: return logAndReturnDefault("Analyzing element of type $javaClass with no containing file\nText:\n${getText()}")
val virtualFile = containingFile.getOriginalFile().getVirtualFile()
?: return logAndReturnDefault("Analyzing non-physical file $containingFile of type ${containingFile.javaClass}")
return getModuleInfoByVirtualFile(project, virtualFile)
}
private fun getModuleInfoByVirtualFile(project: Project, virtualFile: VirtualFile): IdeaModuleInfo {
val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)
val module = projectFileIndex.getModuleForFile(virtualFile)
if (module != null) return module.toSourceInfo()
val orderEntries = projectFileIndex.getOrderEntriesForFile(virtualFile)
val libraryOrSdkEntries = orderEntries.filterIsInstance(javaClass<LibraryOrSdkOrderEntry>())
@entries for (libraryOrSdkOrderEntry in libraryOrSdkEntries) {
when (libraryOrSdkOrderEntry) {
is LibraryOrderEntry -> {
val library = libraryOrSdkOrderEntry.getLibrary() ?: continue @entries
if (projectFileIndex.isInLibrarySource(virtualFile)) {
return LibrarySourceInfo(project, library)
}
else {
return LibraryInfo(project, library)
}
}
is JdkOrderEntry -> {
val sdk = libraryOrSdkOrderEntry.getJdk() ?: continue @entries
return SdkInfo(project, sdk)
}
}
}
return NotUnderContentRootModuleInfo
}
private fun KotlinLightElement<*, *>.getModuleInfoForLightElement(): IdeaModuleInfo {
val element = origin ?: when (this) {
is FakeLightClassForFileOfPackage -> this.getContainingFile()!!
is KotlinLightClassForPackage -> this.getFiles().first()
else -> throw IllegalStateException("Unknown light class without origin is referenced by IDE lazy resolve: $javaClass")
}
return element.getModuleInfo()
}
@@ -20,6 +20,7 @@ import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.asJava.AsJavaPackage;
import org.jetbrains.jet.lang.psi.JetDeclaration;
@@ -31,6 +32,8 @@ import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage;
import org.jetbrains.jet.plugin.project.TargetPlatform;
import org.jetbrains.jet.plugin.project.TargetPlatformDetector;
import static org.jetbrains.jet.plugin.caches.resolve.ResolvePackage.getModuleInfo;
public class DuplicateJvmSignatureAnnotator implements Annotator {
@Override
@@ -42,7 +45,8 @@ public class DuplicateJvmSignatureAnnotator implements Annotator {
if (!(file instanceof JetFile) || TargetPlatformDetector.getPlatform((JetFile) file) != TargetPlatform.JVM) return;
Diagnostics otherDiagnostics = ResolvePackage.getBindingContext((JetElement) element).getDiagnostics();
Diagnostics diagnostics = AsJavaPackage.getJvmSignatureDiagnostics(element, otherDiagnostics);
GlobalSearchScope moduleScope = getModuleInfo(element).contentScope();
Diagnostics diagnostics = AsJavaPackage.getJvmSignatureDiagnostics(element, otherDiagnostics, moduleScope);
if (diagnostics == null) return;
JetPsiChecker.annotateElement(element, holder, diagnostics);
@@ -1,37 +0,0 @@
/*
* Copyright 2010-2013 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.jet.plugin.project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.analyzer.AnalyzerFacade;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM;
public final class AnalyzerFacadeProvider {
private AnalyzerFacadeProvider() {
}
@NotNull
public static AnalyzerFacade getAnalyzerFacadeForFile(@NotNull JetFile file) {
return getAnalyzerFacade(TargetPlatformDetector.getPlatform(file));
}
@NotNull
public static AnalyzerFacade getAnalyzerFacade(@NotNull TargetPlatform targetPlatform) {
return targetPlatform == TargetPlatform.JVM ? AnalyzerFacadeForJVM.INSTANCE : JSAnalyzerFacadeForIDEA.INSTANCE;
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2014 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.jet.plugin.project
import org.jetbrains.jet.analyzer.AnalyzerFacade
import org.jetbrains.jet.lang.resolve.java.JvmPlatformParameters
import org.jetbrains.jet.analyzer.ResolverForModule
import org.jetbrains.jet.lang.resolve.java.JvmAnalyzerFacade
import org.jetbrains.jet.plugin.caches.resolve.JsAnalyzerFacade
public object AnalyzerFacadeProvider {
//NOTE: it's convenient that JS backend doesn't have platform parameters (for now)
// otherwise we would be forced to add casts on the call site of setupResolverForProject
public fun getAnalyzerFacade(targetPlatform: TargetPlatform): AnalyzerFacade<ResolverForModule, JvmPlatformParameters> {
return when (targetPlatform) {
TargetPlatform.JVM -> JvmAnalyzerFacade
TargetPlatform.JS -> JsAnalyzerFacade
else -> throw IllegalArgumentException("Unsupported platfrom: $targetPlatform")
}
}
}
@@ -1,40 +0,0 @@
/*
* Copyright 2010-2013 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.jet.plugin.project;
import com.intellij.openapi.project.Project;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.analyzer.AnalyzerFacade;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.k2js.analyze.AnalyzerFacadeForJS;
import java.util.Collection;
public enum JSAnalyzerFacadeForIDEA implements AnalyzerFacade {
INSTANCE;
private JSAnalyzerFacadeForIDEA() {
}
@NotNull
@Override
public Setup createSetup(@NotNull Project project, @NotNull Collection<JetFile> syntheticFiles, @NotNull GlobalSearchScope filesScope) {
return new BasicSetup(AnalyzerFacadeForJS.getLazyResolveSession(syntheticFiles, filesScope, new IDEAConfig(project)));
}
}
@@ -21,12 +21,11 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.analyzer.AnalyzerFacade;
import org.jetbrains.jet.lang.psi.JetFile;
public class TargetPlatformDetector {
public static final TargetPlatformDetector INSTANCE = new TargetPlatformDetector();
private static final Logger LOG = Logger.getInstance(AnalyzerFacade.class);
private static final Logger LOG = Logger.getInstance(TargetPlatformDetector.class);
private TargetPlatformDetector() {
}