Reformat caches package

This commit is contained in:
Nikolay Krasko
2018-02-16 19:14:37 +03:00
parent 77b146a51a
commit 47f4c79204
28 changed files with 675 additions and 624 deletions
@@ -25,13 +25,13 @@ data class CachedAttributeData<out T>(val value: T, val timeStamp: Long)
interface FileAttributeService { interface FileAttributeService {
fun register(id: String, version: Int, fixedSize: Boolean = true) {} fun register(id: String, version: Int, fixedSize: Boolean = true) {}
fun <T: Enum<T>> writeEnumAttribute(id: String, file: VirtualFile, value: T): CachedAttributeData<T> = fun <T : Enum<T>> writeEnumAttribute(id: String, file: VirtualFile, value: T): CachedAttributeData<T> =
CachedAttributeData(value, timeStamp = file.timeStamp) CachedAttributeData(value, timeStamp = file.timeStamp)
fun <T: Enum<T>> readEnumAttribute(id: String, file: VirtualFile, klass: Class<T>): CachedAttributeData<T>? = null fun <T : Enum<T>> readEnumAttribute(id: String, file: VirtualFile, klass: Class<T>): CachedAttributeData<T>? = null
fun writeBooleanAttribute(id: String, file: VirtualFile, value: Boolean): CachedAttributeData<Boolean> = fun writeBooleanAttribute(id: String, file: VirtualFile, value: Boolean): CachedAttributeData<Boolean> =
CachedAttributeData(value, timeStamp = file.timeStamp) CachedAttributeData(value, timeStamp = file.timeStamp)
fun readBooleanAttribute(id: String, file: VirtualFile): CachedAttributeData<Boolean>? = null fun readBooleanAttribute(id: String, file: VirtualFile): CachedAttributeData<Boolean>? = null
@@ -63,10 +63,10 @@ class KotlinShortNamesCache(private val project: Project) : PsiShortNamesCache()
val allFqNames = HashSet<FqName?>() val allFqNames = HashSet<FqName?>()
KotlinClassShortNameIndex.getInstance().get(name, project, effectiveScope) KotlinClassShortNameIndex.getInstance().get(name, project, effectiveScope)
.mapTo(allFqNames) { it.fqName } .mapTo(allFqNames) { it.fqName }
KotlinFileFacadeShortNameIndex.INSTANCE.get(name, project, effectiveScope) KotlinFileFacadeShortNameIndex.INSTANCE.get(name, project, effectiveScope)
.mapTo(allFqNames) { it.javaFileFacadeFqName } .mapTo(allFqNames) { it.javaFileFacadeFqName }
val result = ArrayList<PsiClass>() val result = ArrayList<PsiClass>()
for (fqName in allFqNames) { for (fqName in allFqNames) {
@@ -111,15 +111,15 @@ class KotlinShortNamesCache(private val project: Project) : PsiShortNamesCache()
val functionIndex = KotlinFunctionShortNameIndex.getInstance() val functionIndex = KotlinFunctionShortNameIndex.getInstance()
val kotlinFunctionsPsi = functionIndex.get(name, project, scope).asSequence() val kotlinFunctionsPsi = functionIndex.get(name, project, scope).asSequence()
.flatMap { LightClassUtil.getLightClassMethods(it).asSequence() } .flatMap { LightClassUtil.getLightClassMethods(it).asSequence() }
.filter { it.name == name } .filter { it.name == name }
val propertyAccessorsPsi = sequenceOfLazyValues({ getPropertyNamesCandidatesByAccessorName(Name.identifier(name)) }) val propertyAccessorsPsi = sequenceOfLazyValues({ getPropertyNamesCandidatesByAccessorName(Name.identifier(name)) })
.flatMap { it.asSequence() } .flatMap { it.asSequence() }
.flatMap { propertiesIndex.get(it.asString(), project, scope).asSequence() } .flatMap { propertiesIndex.get(it.asString(), project, scope).asSequence() }
.flatMap { it.getAccessorLightMethods().allDeclarations.asSequence() } .flatMap { it.getAccessorLightMethods().allDeclarations.asSequence() }
.filter { it.name == name } .filter { it.name == name }
.map { it as? PsiMethod } .map { it as? PsiMethod }
return sequenceOfLazyValues({ kotlinFunctionsPsi }, { propertyAccessorsPsi }).flatMap { it }.filterNotNull() return sequenceOfLazyValues({ kotlinFunctionsPsi }, { propertyAccessorsPsi }).flatMap { it }.filterNotNull()
} }
@@ -142,8 +142,8 @@ class KotlinShortNamesCache(private val project: Project) : PsiShortNamesCache()
return limitedByMaxCount.toTypedArray() return limitedByMaxCount.toTypedArray()
} }
override fun processMethodsWithName(name: String, scope: GlobalSearchScope, processor: Processor<PsiMethod>): Boolean override fun processMethodsWithName(name: String, scope: GlobalSearchScope, processor: Processor<PsiMethod>): Boolean =
= ContainerUtil.process(getMethodsByName(name, scope), processor) ContainerUtil.process(getMethodsByName(name, scope), processor)
override fun getAllMethodNames(): Array<String> { override fun getAllMethodNames(): Array<String> {
val functionIndex = KotlinFunctionShortNameIndex.getInstance() val functionIndex = KotlinFunctionShortNameIndex.getInstance()
@@ -151,7 +151,7 @@ class KotlinShortNamesCache(private val project: Project) : PsiShortNamesCache()
val propertiesIndex = KotlinPropertyShortNameIndex.getInstance() val propertiesIndex = KotlinPropertyShortNameIndex.getInstance()
val propertyAccessorNames = propertiesIndex.getAllKeys(project) val propertyAccessorNames = propertiesIndex.getAllKeys(project)
.flatMap(::getAccessorNamesCandidatesByPropertyName) .flatMap(::getAccessorNamesCandidatesByPropertyName)
return (functionNames + propertyAccessorNames).toTypedArray() return (functionNames + propertyAccessorNames).toTypedArray()
} }
@@ -162,8 +162,8 @@ class KotlinShortNamesCache(private val project: Project) : PsiShortNamesCache()
private fun getFieldSequenceByName(name: String, scope: GlobalSearchScope): Sequence<PsiField> { private fun getFieldSequenceByName(name: String, scope: GlobalSearchScope): Sequence<PsiField> {
return KotlinPropertyShortNameIndex.getInstance().get(name, project, scope).asSequence() return KotlinPropertyShortNameIndex.getInstance().get(name, project, scope).asSequence()
.map { LightClassUtil.getLightClassBackingField(it) } .map { LightClassUtil.getLightClassBackingField(it) }
.filterNotNull() .filterNotNull()
} }
override fun getFieldsByName(name: String, scope: GlobalSearchScope): Array<PsiField> { override fun getFieldsByName(name: String, scope: GlobalSearchScope): Array<PsiField> {
@@ -46,9 +46,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap import java.util.concurrent.ConcurrentMap
class KotlinPackageContentModificationListener( class KotlinPackageContentModificationListener(private val project: Project) {
private val project: Project
) {
init { init {
val connection = project.messageBus.connect() val connection = project.messageBus.connect()
@@ -57,22 +55,21 @@ class KotlinPackageContentModificationListener(
override fun after(events: List<VFileEvent>) = onEvents(events) override fun after(events: List<VFileEvent>) = onEvents(events)
private fun isRelevant(it: VFileEvent): Boolean = private fun isRelevant(it: VFileEvent): Boolean =
it is VFileMoveEvent || it is VFileCreateEvent || it is VFileCopyEvent || it is VFileDeleteEvent it is VFileMoveEvent || it is VFileCreateEvent || it is VFileCopyEvent || it is VFileDeleteEvent
fun onEvents(events: List<VFileEvent>) { fun onEvents(events: List<VFileEvent>) {
val service = ServiceManager.getService(project, PerModulePackageCacheService::class.java) val service = ServiceManager.getService(project, PerModulePackageCacheService::class.java)
if (events.size >= FULL_DROP_THRESHOLD) { if (events.size >= FULL_DROP_THRESHOLD) {
service.onTooComplexChange() service.onTooComplexChange()
} } else {
else {
events events
.asSequence() .asSequence()
.filter { it.file != null } .filter { it.file != null }
.filter(::isRelevant) .filter(::isRelevant)
.mapNotNull { it.file } .mapNotNull { it.file }
.filter { it.isDirectory || FileTypeRegistry.getInstance().getFileTypeByFileName(it.name) == KotlinFileType.INSTANCE } .filter { it.isDirectory || FileTypeRegistry.getInstance().getFileTypeByFileName(it.name) == KotlinFileType.INSTANCE }
.forEach { file -> service.notifyPackageChange(file) } .forEach { file -> service.notifyPackageChange(file) }
} }
} }
}) })
@@ -139,8 +136,7 @@ class PerModulePackageCacheService(private val project: Project) {
private fun checkPendingChanges() = synchronized(this) { private fun checkPendingChanges() = synchronized(this) {
if (pendingVFileChanges.size + pendingKtFileChanges.size >= FULL_DROP_THRESHOLD) { if (pendingVFileChanges.size + pendingKtFileChanges.size >= FULL_DROP_THRESHOLD) {
onTooComplexChange() onTooComplexChange()
} } else {
else {
pendingVFileChanges.forEach { vfile -> pendingVFileChanges.forEach { vfile ->
// When VirtualFile !isValid (deleted for example), it impossible to use getModuleInfoByVirtualFile // When VirtualFile !isValid (deleted for example), it impossible to use getModuleInfoByVirtualFile
@@ -149,13 +145,12 @@ class PerModulePackageCacheService(private val project: Project) {
for ((module, data) in cache) { for ((module, data) in cache) {
val sourceRootUrls = module.rootManager.sourceRootUrls val sourceRootUrls = module.rootManager.sourceRootUrls
if (sourceRootUrls.any { url -> if (sourceRootUrls.any { url ->
vfile.containedInOrContains(url) vfile.containedInOrContains(url)
}) { }) {
data.clear() data.clear()
} }
} }
} } else {
else {
(getModuleInfoByVirtualFile(project, vfile) as? ModuleSourceInfo)?.let { (getModuleInfoByVirtualFile(project, vfile) as? ModuleSourceInfo)?.let {
invalidateCacheForModuleSourceInfo(it) invalidateCacheForModuleSourceInfo(it)
} }
@@ -174,8 +169,8 @@ class PerModulePackageCacheService(private val project: Project) {
} }
private fun VirtualFile.containedInOrContains(root: String) = private fun VirtualFile.containedInOrContains(root: String) =
(VfsUtilCore.isEqualOrAncestor(url, root) (VfsUtilCore.isEqualOrAncestor(url, root)
|| isDirectory && VfsUtilCore.isEqualOrAncestor(root, url)) || isDirectory && VfsUtilCore.isEqualOrAncestor(root, url))
fun packageExists(packageFqName: FqName, moduleInfo: ModuleSourceInfo): Boolean { fun packageExists(packageFqName: FqName, moduleInfo: ModuleSourceInfo): Boolean {
@@ -35,12 +35,11 @@ import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor
import javax.inject.Inject import javax.inject.Inject
class CodeFragmentAnalyzer( class CodeFragmentAnalyzer(
private val resolveSession: ResolveSession, private val resolveSession: ResolveSession,
private val qualifierResolver: QualifiedExpressionResolver, private val qualifierResolver: QualifiedExpressionResolver,
private val expressionTypingServices: ExpressionTypingServices, private val expressionTypingServices: ExpressionTypingServices,
private val typeResolver: TypeResolver private val typeResolver: TypeResolver
) { ) {
// component dependency cycle // component dependency cycle
var resolveElementCache: ResolveElementCache? = null var resolveElementCache: ResolveElementCache? = null
@Inject set @Inject set
@@ -54,20 +53,28 @@ class CodeFragmentAnalyzer(
when (codeFragmentElement) { when (codeFragmentElement) {
is KtExpression -> { is KtExpression -> {
PreliminaryDeclarationVisitor.createForExpression(codeFragmentElement, trace, PreliminaryDeclarationVisitor.createForExpression(
expressionTypingServices.languageVersionSettings) codeFragmentElement, trace,
expressionTypingServices.languageVersionSettings
)
expressionTypingServices.getTypeInfo( expressionTypingServices.getTypeInfo(
scopeForContextElement, scopeForContextElement,
codeFragmentElement, codeFragmentElement,
TypeUtils.NO_EXPECTED_TYPE, TypeUtils.NO_EXPECTED_TYPE,
dataFlowInfo, dataFlowInfo,
trace, trace,
false false
) )
} }
is KtTypeReference -> { is KtTypeReference -> {
val context = TypeResolutionContext(scopeForContextElement, trace, true, true, codeFragment.suppressDiagnosticsInDebugMode()).noBareTypes() val context = TypeResolutionContext(
scopeForContextElement,
trace,
true,
true,
codeFragment.suppressDiagnosticsInDebugMode()
).noBareTypes()
typeResolver.resolvePossiblyBareType(context, codeFragmentElement) typeResolver.resolvePossiblyBareType(context, codeFragmentElement)
} }
} }
@@ -76,17 +83,17 @@ class CodeFragmentAnalyzer(
//TODO: this code should be moved into debugger which should set correct context for its code fragment //TODO: this code should be moved into debugger which should set correct context for its code fragment
private fun KtElement.correctContextForElement(): KtElement { private fun KtElement.correctContextForElement(): KtElement {
return when (this) { return when (this) {
is KtProperty -> this.delegateExpressionOrInitializer is KtProperty -> this.delegateExpressionOrInitializer
is KtFunctionLiteral -> this.bodyExpression?.statements?.lastOrNull() is KtFunctionLiteral -> this.bodyExpression?.statements?.lastOrNull()
is KtDeclarationWithBody -> this.bodyExpression is KtDeclarationWithBody -> this.bodyExpression
is KtBlockExpression -> this.statements.lastOrNull() is KtBlockExpression -> this.statements.lastOrNull()
else -> null else -> null
} ?: this } ?: this
} }
private fun getScopeAndDataFlowForAnalyzeFragment( private fun getScopeAndDataFlowForAnalyzeFragment(
codeFragment: KtCodeFragment, codeFragment: KtCodeFragment,
resolveToElement: (KtElement) -> BindingContext resolveToElement: (KtElement) -> BindingContext
): Pair<LexicalScope, DataFlowInfo>? { ): Pair<LexicalScope, DataFlowInfo>? {
val context = codeFragment.context val context = codeFragment.context
@@ -103,7 +110,8 @@ class CodeFragmentAnalyzer(
when (context) { when (context) {
is KtPrimaryConstructor -> { is KtPrimaryConstructor -> {
val descriptor = (getClassDescriptor(context.getContainingClassOrObject()) as? ClassDescriptorWithResolutionScopes) ?: return null val descriptor =
(getClassDescriptor(context.getContainingClassOrObject()) as? ClassDescriptorWithResolutionScopes) ?: return null
scopeForContextElement = descriptor.scopeForInitializerResolution scopeForContextElement = descriptor.scopeForInitializerResolution
dataFlowInfo = DataFlowInfo.EMPTY dataFlowInfo = DataFlowInfo.EMPTY
@@ -145,8 +153,10 @@ class CodeFragmentAnalyzer(
} }
val importScopes = importList.imports.mapNotNull { val importScopes = importList.imports.mapNotNull {
qualifierResolver.processImportReference(it, resolveSession.moduleDescriptor, resolveSession.trace, qualifierResolver.processImportReference(
excludedImportNames = emptyList(), packageFragmentForVisibilityCheck = null) it, resolveSession.moduleDescriptor, resolveSession.trace,
excludedImportNames = emptyList(), packageFragmentForVisibilityCheck = null
)
} }
return scopeForContextElement.addImportingScopes(importScopes) to dataFlowInfo return scopeForContextElement.addImportingScopes(importScopes) to dataFlowInfo
@@ -54,20 +54,19 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
override fun createDataHolderForClass(classOrObject: KtClassOrObject, builder: LightClassBuilder): LightClassDataHolder.ForClass { override fun createDataHolderForClass(classOrObject: KtClassOrObject, builder: LightClassBuilder): LightClassDataHolder.ForClass {
return if (classOrObject.isLocal) { return if (classOrObject.isLocal) {
LazyLightClassDataHolder.ForClass( LazyLightClassDataHolder.ForClass(
builder, builder,
classOrObject.project, classOrObject.project,
exactContextProvider = { IDELightClassContexts.contextForLocalClassOrObject(classOrObject) }, exactContextProvider = { IDELightClassContexts.contextForLocalClassOrObject(classOrObject) },
dummyContextProvider = null, dummyContextProvider = null,
isLocal = true isLocal = true
) )
} } else {
else {
LazyLightClassDataHolder.ForClass( LazyLightClassDataHolder.ForClass(
builder, builder,
classOrObject.project, classOrObject.project,
exactContextProvider = { IDELightClassContexts.contextForNonLocalClassOrObject(classOrObject) }, exactContextProvider = { IDELightClassContexts.contextForNonLocalClassOrObject(classOrObject) },
dummyContextProvider = { IDELightClassContexts.lightContextForClassOrObject(classOrObject) }, dummyContextProvider = { IDELightClassContexts.lightContextForClassOrObject(classOrObject) },
isLocal = false isLocal = false
) )
} }
} }
@@ -79,19 +78,19 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
val sortedFiles = files.sortedWith(scopeFileComparator) val sortedFiles = files.sortedWith(scopeFileComparator)
return LazyLightClassDataHolder.ForFacade( return LazyLightClassDataHolder.ForFacade(
builder, builder,
files.first().project, files.first().project,
exactContextProvider = { IDELightClassContexts.contextForFacade(sortedFiles) }, exactContextProvider = { IDELightClassContexts.contextForFacade(sortedFiles) },
dummyContextProvider = { IDELightClassContexts.lightContextForFacade(sortedFiles) } dummyContextProvider = { IDELightClassContexts.lightContextForFacade(sortedFiles) }
) )
} }
override fun createDataHolderForScript(script: KtScript, builder: LightClassBuilder): LightClassDataHolder.ForScript { override fun createDataHolderForScript(script: KtScript, builder: LightClassBuilder): LightClassDataHolder.ForScript {
return LazyLightClassDataHolder.ForScript( return LazyLightClassDataHolder.ForScript(
builder, builder,
script.project, script.project,
exactContextProvider = { IDELightClassContexts.contextForScript(script) }, exactContextProvider = { IDELightClassContexts.contextForScript(script) },
dummyContextProvider = { IDELightClassContexts.lightContextForScript(script) } dummyContextProvider = { IDELightClassContexts.lightContextForScript(script) }
) )
} }
@@ -108,11 +107,12 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
} }
override fun findClassOrObjectDeclarationsInPackage( override fun findClassOrObjectDeclarationsInPackage(
packageFqName: FqName, packageFqName: FqName,
searchScope: GlobalSearchScope searchScope: GlobalSearchScope
): Collection<KtClassOrObject> { ): Collection<KtClassOrObject> {
return KotlinTopLevelClassByPackageIndex.getInstance().get( return KotlinTopLevelClassByPackageIndex.getInstance().get(
packageFqName.asString(), project, sourceAndClassFiles(searchScope, project)) packageFqName.asString(), project, sourceAndClassFiles(searchScope, project)
)
} }
override fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean { override fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean {
@@ -136,7 +136,8 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
} }
} }
if ((classOrObject.containingFile as? KtFile)?.analysisContext != null || if ((classOrObject.containingFile as? KtFile)?.analysisContext != null ||
classOrObject.containingFile.originalFile.virtualFile != null) { classOrObject.containingFile.originalFile.virtualFile != null
) {
// explicit request to create light class from dummy.kt // explicit request to create light class from dummy.kt
return KtLightClassForSourceDeclaration.create(classOrObject) return KtLightClassForSourceDeclaration.create(classOrObject)
} }
@@ -146,8 +147,8 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
override fun getLightClassForScript(script: KtScript): KtLightClassForScript? = KtLightClassForScript.create(script) override fun getLightClassForScript(script: KtScript): KtLightClassForScript? = KtLightClassForScript.create(script)
private fun withFakeLightClasses( private fun withFakeLightClasses(
lightClassForFacade: KtLightClassForFacade?, lightClassForFacade: KtLightClassForFacade?,
facadeFiles: List<KtFile> facadeFiles: List<KtFile>
): List<PsiClass> { ): List<PsiClass> {
if (lightClassForFacade == null) return emptyList() if (lightClassForFacade == null) return emptyList()
@@ -191,8 +192,7 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
val partClassFile = facadeKtFile.virtualFile.parent.findChild(partClassFileShortName) ?: return@mapNotNull null val partClassFile = facadeKtFile.virtualFile.parent.findChild(partClassFileShortName) ?: return@mapNotNull null
val javaClsClass = createClsJavaClassFromVirtualFile(facadeKtFile, partClassFile, null) ?: return@mapNotNull null val javaClsClass = createClsJavaClassFromVirtualFile(facadeKtFile, partClassFile, null) ?: return@mapNotNull null
KtLightClassForDecompiledDeclaration(javaClsClass, null, facadeKtFile) KtLightClassForDecompiledDeclaration(javaClsClass, null, facadeKtFile)
} } else {
else {
// TODO should we build light classes for parts from source? // TODO should we build light classes for parts from source?
null null
} }
@@ -204,18 +204,18 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
} }
fun createLightClassForFileFacade( fun createLightClassForFileFacade(
facadeFqName: FqName, facadeFqName: FqName,
facadeFiles: List<KtFile>, facadeFiles: List<KtFile>,
moduleInfo: IdeaModuleInfo moduleInfo: IdeaModuleInfo
): List<PsiClass> { ): List<PsiClass> {
val (clsFiles, sourceFiles) = facadeFiles.partition { it is KtClsFile } val (clsFiles, sourceFiles) = facadeFiles.partition { it is KtClsFile }
val lightClassesForClsFacades = clsFiles.mapNotNull { createLightClassForDecompiledKotlinFile(it as KtClsFile) } val lightClassesForClsFacades = clsFiles.mapNotNull { createLightClassForDecompiledKotlinFile(it as KtClsFile) }
if (moduleInfo is ModuleSourceInfo && sourceFiles.isNotEmpty()) { if (moduleInfo is ModuleSourceInfo && sourceFiles.isNotEmpty()) {
val lightClassForFacade = KtLightClassForFacade.createForFacade( val lightClassForFacade = KtLightClassForFacade.createForFacade(
psiManager, facadeFqName, moduleInfo.contentScope(), sourceFiles) psiManager, facadeFqName, moduleInfo.contentScope(), sourceFiles
)
return withFakeLightClasses(lightClassForFacade, sourceFiles) + lightClassesForClsFacades return withFakeLightClasses(lightClassForFacade, sourceFiles) + lightClassesForClsFacades
} } else {
else {
return lightClassesForClsFacades return lightClassesForClsFacades
} }
} }
@@ -229,8 +229,7 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
override fun resolveToDescriptor(declaration: KtDeclaration): DeclarationDescriptor? { override fun resolveToDescriptor(declaration: KtDeclaration): DeclarationDescriptor? {
try { try {
return declaration.resolveToDescriptorIfAny(BodyResolveMode.FULL) return declaration.resolveToDescriptorIfAny(BodyResolveMode.FULL)
} } catch (e: NoDescriptorForDeclarationException) {
catch (e: NoDescriptorForDeclarationException) {
return null return null
} }
} }
@@ -272,8 +271,8 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
} }
private fun findCorrespondingLightClass( private fun findCorrespondingLightClass(
decompiledClassOrObject: KtClassOrObject, decompiledClassOrObject: KtClassOrObject,
rootLightClassForDecompiledFile: KtLightClassForDecompiledDeclaration rootLightClassForDecompiledFile: KtLightClassForDecompiledDeclaration
): KtLightClassForDecompiledDeclaration { ): KtLightClassForDecompiledDeclaration {
val relativeFqName = getClassRelativeName(decompiledClassOrObject) val relativeFqName = getClassRelativeName(decompiledClassOrObject)
val iterator = relativeFqName.pathSegments().iterator() val iterator = relativeFqName.pathSegments().iterator()
@@ -306,17 +305,17 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
val classOrObject = file.declarations.filterIsInstance<KtClassOrObject>().singleOrNull() val classOrObject = file.declarations.filterIsInstance<KtClassOrObject>().singleOrNull()
val javaClsClass = createClsJavaClassFromVirtualFile( val javaClsClass = createClsJavaClassFromVirtualFile(
file, virtualFile, file, virtualFile,
correspondingClassOrObject = classOrObject correspondingClassOrObject = classOrObject
) ?: return null ) ?: return null
return KtLightClassForDecompiledDeclaration(javaClsClass, classOrObject, file) return KtLightClassForDecompiledDeclaration(javaClsClass, classOrObject, file)
} }
private fun createClsJavaClassFromVirtualFile( private fun createClsJavaClassFromVirtualFile(
mirrorFile: KtFile, mirrorFile: KtFile,
classFile: VirtualFile, classFile: VirtualFile,
correspondingClassOrObject: KtClassOrObject? correspondingClassOrObject: KtClassOrObject?
): ClsClassImpl? { ): ClsClassImpl? {
val javaFileStub = ClsJavaStubByVirtualFileCache.getInstance(project).get(classFile) ?: return null val javaFileStub = ClsJavaStubByVirtualFileCache.getInstance(project).get(classFile) ?: return null
javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE javaFileStub.psiFactory = ClsWrapperStubPsiFactory.INSTANCE
@@ -363,7 +362,8 @@ class KtFileClassProviderImpl(val lightClassGenerationSupport: LightClassGenerat
file.hasTopLevelCallables() -> file.hasTopLevelCallables() ->
(lightClassGenerationSupport as IDELightClassGenerationSupport).createLightClassForFileFacade( (lightClassGenerationSupport as IDELightClassGenerationSupport).createLightClassForFileFacade(
fileClassFqName, listOf(file), moduleInfo) fileClassFqName, listOf(file), moduleInfo
)
else -> emptyList<PsiClass>() else -> emptyList<PsiClass>()
} }
@@ -26,15 +26,15 @@ import org.jetbrains.kotlin.name.ClassId
class IDEPackagePartProvider(val scope: GlobalSearchScope) : PackagePartProvider { class IDEPackagePartProvider(val scope: GlobalSearchScope) : PackagePartProvider {
override fun findPackageParts(packageFqName: String): List<String> = override fun findPackageParts(packageFqName: String): List<String> =
getPackageParts(packageFqName).flatMap(PackageParts::parts).distinct() getPackageParts(packageFqName).flatMap(PackageParts::parts).distinct()
override fun findMetadataPackageParts(packageFqName: String): List<String> = override fun findMetadataPackageParts(packageFqName: String): List<String> =
getPackageParts(packageFqName).flatMap(PackageParts::metadataParts).distinct() getPackageParts(packageFqName).flatMap(PackageParts::metadataParts).distinct()
private fun getPackageParts(packageFqName: String): MutableList<PackageParts> = private fun getPackageParts(packageFqName: String): MutableList<PackageParts> =
FileBasedIndex.getInstance().getValues(KotlinModuleMappingIndex.KEY, packageFqName, scope) FileBasedIndex.getInstance().getValues(KotlinModuleMappingIndex.KEY, packageFqName, scope)
// Note that in case of several modules with the same name, we return all annotations on all of them, which is probably incorrect // Note that in case of several modules with the same name, we return all annotations on all of them, which is probably incorrect
override fun getAnnotationsOnBinaryModule(moduleName: String): List<ClassId> = override fun getAnnotationsOnBinaryModule(moduleName: String): List<ClassId> =
FileBasedIndex.getInstance().getValues(KotlinJvmModuleAnnotationsIndex.KEY, moduleName, scope).flatten() FileBasedIndex.getInstance().getValues(KotlinJvmModuleAnnotationsIndex.KEY, moduleName, scope).flatten()
} }
@@ -68,8 +68,7 @@ private fun orderEntryToModuleInfo(project: Project, orderEntry: OrderEntry, for
val module = orderEntry.module ?: return emptyList() val module = orderEntry.module ?: return emptyList()
if (forProduction && orderEntry is ModuleOrderEntryImpl && orderEntry.isProductionOnTestDependency) { if (forProduction && orderEntry is ModuleOrderEntryImpl && orderEntry.isProductionOnTestDependency) {
listOfNotNull(module.testSourceInfo()) listOfNotNull(module.testSourceInfo())
} } else {
else {
module.toInfos() module.toInfos()
} }
} }
@@ -93,10 +92,10 @@ fun <T> Module.cached(provider: CachedValueProvider<T>): T {
private fun OrderEntry.acceptAsDependency(forProduction: Boolean): Boolean { private fun OrderEntry.acceptAsDependency(forProduction: Boolean): Boolean {
return this !is ExportableOrderEntry return this !is ExportableOrderEntry
|| !forProduction || !forProduction
// this is needed for Maven/Gradle projects with "production-on-test" dependency // this is needed for Maven/Gradle projects with "production-on-test" dependency
|| this is ModuleOrderEntryImpl && isProductionOnTestDependency || this is ModuleOrderEntryImpl && isProductionOnTestDependency
|| scope.isForProductionCompile || scope.isForProductionCompile
} }
private fun ideaModelDependencies(module: Module, forProduction: Boolean): List<IdeaModuleInfo> { private fun ideaModelDependencies(module: Module, forProduction: Boolean): List<IdeaModuleInfo> {
@@ -143,7 +142,7 @@ interface ModuleSourceInfo : IdeaModuleInfo, TrackableModuleInfo {
get() = TargetPlatformDetector.getPlatform(module) get() = TargetPlatformDetector.getPlatform(module)
override fun createModificationTracker(): ModificationTracker = override fun createModificationTracker(): ModificationTracker =
KotlinModuleModificationTracker(module) KotlinModuleModificationTracker(module)
} }
sealed class ModuleSourceInfoWithExpectedBy(private val forProduction: Boolean) : ModuleSourceInfo { sealed class ModuleSourceInfoWithExpectedBy(private val forProduction: Boolean) : ModuleSourceInfo {
@@ -157,7 +156,8 @@ sealed class ModuleSourceInfoWithExpectedBy(private val forProduction: Boolean)
override fun dependencies(): List<IdeaModuleInfo> = module.cached(createCachedValueProvider { override fun dependencies(): List<IdeaModuleInfo> = module.cached(createCachedValueProvider {
CachedValueProvider.Result( CachedValueProvider.Result(
ideaModelDependencies(module, forProduction), ideaModelDependencies(module, forProduction),
ProjectRootModificationTracker.getInstance(module.project)) ProjectRootModificationTracker.getInstance(module.project)
)
}) })
// NB: CachedValueProvider must exist separately in Production / Test source info, // NB: CachedValueProvider must exist separately in Production / Test source info,
@@ -213,7 +213,7 @@ private fun Module.hasProductionRoots() = hasRootsOfType(JavaSourceRootType.SOUR
private fun Module.hasTestRoots() = hasRootsOfType(JavaSourceRootType.TEST_SOURCE) private fun Module.hasTestRoots() = hasRootsOfType(JavaSourceRootType.TEST_SOURCE)
private fun Module.hasRootsOfType(sourceRootType: JavaSourceRootType): Boolean = private fun Module.hasRootsOfType(sourceRootType: JavaSourceRootType): Boolean =
rootManager.contentEntries.any { it.getSourceFolders(sourceRootType).isNotEmpty() } rootManager.contentEntries.any { it.getSourceFolders(sourceRootType).isNotEmpty() }
private abstract class ModuleSourceScope(val module: Module) : GlobalSearchScope(module.project), GlobalSearchScopeWithModuleSources { private abstract class ModuleSourceScope(val module: Module) : GlobalSearchScope(module.project), GlobalSearchScopeWithModuleSources {
override fun compare(file1: VirtualFile, file2: VirtualFile) = 0 override fun compare(file1: VirtualFile, file2: VirtualFile) = 0
@@ -228,10 +228,12 @@ private class ModuleProductionSourceScope(module: Module) : ModuleSourceScope(mo
if (this === other) return true if (this === other) return true
return (other is ModuleProductionSourceScope && module == other.module) return (other is ModuleProductionSourceScope && module == other.module)
} }
// KT-6206 // KT-6206
override fun hashCode(): Int = 31 * module.hashCode() override fun hashCode(): Int = 31 * module.hashCode()
override fun contains(file: VirtualFile) = moduleFileIndex.isInSourceContentWithoutInjected(file) && !moduleFileIndex.isInTestSourceContent(file) override fun contains(file: VirtualFile) =
moduleFileIndex.isInSourceContentWithoutInjected(file) && !moduleFileIndex.isInTestSourceContent(file)
override fun toString() = "ModuleProductionSourceScope($module)" override fun toString() = "ModuleProductionSourceScope($module)"
} }
@@ -243,6 +245,7 @@ private class ModuleTestSourceScope(module: Module) : ModuleSourceScope(module)
if (this === other) return true if (this === other) return true
return (other is ModuleTestSourceScope && module == other.module) return (other is ModuleTestSourceScope && module == other.module)
} }
// KT-6206 // KT-6206
override fun hashCode(): Int = 37 * module.hashCode() override fun hashCode(): Int = 37 * module.hashCode()
@@ -278,7 +281,7 @@ class LibraryInfo(val project: Project, val library: Library) : IdeaModuleInfo,
get() = LibrarySourceInfo(project, library) get() = LibrarySourceInfo(project, library)
override fun getLibraryRoots(): Collection<String> = override fun getLibraryRoots(): Collection<String> =
library.getFiles(OrderRootType.CLASSES).mapNotNull(PathUtil::getLocalPath) library.getFiles(OrderRootType.CLASSES).mapNotNull(PathUtil::getLocalPath)
override fun toString() = "LibraryInfo(libraryName=${library.name})" override fun toString() = "LibraryInfo(libraryName=${library.name})"
@@ -331,7 +334,7 @@ object NotUnderContentRootModuleInfo : IdeaModuleInfo {
} }
private class LibraryWithoutSourceScope(project: Project, private val library: Library) : private class LibraryWithoutSourceScope(project: Project, private val library: Library) :
LibraryScopeBase(project, library.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) { LibraryScopeBase(project, library.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) {
override fun getFileRoot(file: VirtualFile): VirtualFile? = myIndex.getClassRootForFile(file) override fun getFileRoot(file: VirtualFile): VirtualFile? = myIndex.getClassRootForFile(file)
@@ -343,7 +346,7 @@ private class LibraryWithoutSourceScope(project: Project, private val library: L
} }
private class LibrarySourceScope(project: Project, private val library: Library) : private class LibrarySourceScope(project: Project, private val library: Library) :
LibraryScopeBase(project, arrayOf<VirtualFile>(), library.getFiles(OrderRootType.SOURCES)) { LibraryScopeBase(project, arrayOf<VirtualFile>(), library.getFiles(OrderRootType.SOURCES)) {
override fun getFileRoot(file: VirtualFile): VirtualFile? = myIndex.getSourceRootForFile(file) override fun getFileRoot(file: VirtualFile): VirtualFile? = myIndex.getSourceRootForFile(file)
@@ -356,7 +359,7 @@ private class LibrarySourceScope(project: Project, private val library: Library)
//TODO: (module refactoring) android sdk has modified scope //TODO: (module refactoring) android sdk has modified scope
private class SdkScope(project: Project, private val sdk: Sdk) : private class SdkScope(project: Project, private val sdk: Sdk) :
LibraryScopeBase(project, sdk.rootProvider.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) { LibraryScopeBase(project, sdk.rootProvider.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) {
override fun equals(other: Any?) = other is SdkScope && sdk == other.sdk override fun equals(other: Any?) = other is SdkScope && sdk == other.sdk
@@ -91,15 +91,14 @@ fun PsiParameter.getParameterDescriptor(resolutionFacade: ResolutionFacade = jav
} }
fun PsiClass.resolveToDescriptor( fun PsiClass.resolveToDescriptor(
resolutionFacade: ResolutionFacade, resolutionFacade: ResolutionFacade,
declarationTranslator: (KtClassOrObject) -> KtClassOrObject? = { it } declarationTranslator: (KtClassOrObject) -> KtClassOrObject? = { it }
): ClassDescriptor? { ): ClassDescriptor? {
return if (this is KtLightClass && this !is KtLightClassForDecompiledDeclaration) { return if (this is KtLightClass && this !is KtLightClassForDecompiledDeclaration) {
val origin = this.kotlinOrigin ?: return null val origin = this.kotlinOrigin ?: return null
val declaration = declarationTranslator(origin) ?: return null val declaration = declarationTranslator(origin) ?: return null
resolutionFacade.resolveToDescriptor(declaration) resolutionFacade.resolveToDescriptor(declaration)
} } else {
else {
getJavaClassDescriptor(resolutionFacade) getJavaClassDescriptor(resolutionFacade)
} as? ClassDescriptor } as? ClassDescriptor
} }
@@ -143,4 +142,4 @@ private fun <T : DeclarationDescriptorWithSource> Collection<T>.findByJavaElemen
} }
fun PsiElement.javaResolutionFacade() = fun PsiElement.javaResolutionFacade() =
KotlinCacheService.getInstance(project).getResolutionFacadeByFile(this.originalElement.containingFile, JvmPlatform) KotlinCacheService.getInstance(project).getResolutionFacadeByFile(this.originalElement.containingFile, JvmPlatform)
@@ -71,27 +71,27 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
override fun getSuppressionCache(): KotlinSuppressCache = kotlinSuppressCache.value override fun getSuppressionCache(): KotlinSuppressCache = kotlinSuppressCache.value
private val globalFacadesPerPlatformAndSdk: SLRUCache<PlatformAnalysisSettings, GlobalFacade> = private val globalFacadesPerPlatformAndSdk: SLRUCache<PlatformAnalysisSettings, GlobalFacade> =
object : SLRUCache<PlatformAnalysisSettings, GlobalFacade>(2 * 3 * 2, 2 * 3 * 2) { object : SLRUCache<PlatformAnalysisSettings, GlobalFacade>(2 * 3 * 2, 2 * 3 * 2) {
override fun createValue(settings: PlatformAnalysisSettings): GlobalFacade { override fun createValue(settings: PlatformAnalysisSettings): GlobalFacade {
return GlobalFacade(settings) return GlobalFacade(settings)
}
} }
}
private val facadesForScriptDependencies: SLRUCache<ScriptModuleInfo, ProjectResolutionFacade> = private val facadesForScriptDependencies: SLRUCache<ScriptModuleInfo, ProjectResolutionFacade> =
object : SLRUCache<ScriptModuleInfo, ProjectResolutionFacade>(2, 3) { object : SLRUCache<ScriptModuleInfo, ProjectResolutionFacade>(2, 3) {
override fun createValue(scriptModuleInfo: ScriptModuleInfo?): ProjectResolutionFacade { override fun createValue(scriptModuleInfo: ScriptModuleInfo?): ProjectResolutionFacade {
return createFacadeForScriptDependencies(ScriptDependenciesModuleInfo(project, scriptModuleInfo)) return createFacadeForScriptDependencies(ScriptDependenciesModuleInfo(project, scriptModuleInfo))
}
} }
}
private fun getFacadeForScriptDependencies(scriptModuleInfo: ScriptModuleInfo) = synchronized(facadesForScriptDependencies) { private fun getFacadeForScriptDependencies(scriptModuleInfo: ScriptModuleInfo) = synchronized(facadesForScriptDependencies) {
facadesForScriptDependencies.get(scriptModuleInfo) facadesForScriptDependencies.get(scriptModuleInfo)
} }
private fun createFacadeForScriptDependencies( private fun createFacadeForScriptDependencies(
dependenciesModuleInfo: ScriptDependenciesModuleInfo, dependenciesModuleInfo: ScriptDependenciesModuleInfo,
syntheticFiles: Collection<KtFile> = listOf() syntheticFiles: Collection<KtFile> = listOf()
): ProjectResolutionFacade { ): ProjectResolutionFacade {
val sdk = findJdk(dependenciesModuleInfo.scriptModuleInfo?.externalDependencies, project) val sdk = findJdk(dependenciesModuleInfo.scriptModuleInfo?.externalDependencies, project)
val platform = JvmPlatform // TODO: Js scripts? val platform = JvmPlatform // TODO: Js scripts?
@@ -99,18 +99,18 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
val sdkFacade = GlobalFacade(facadeKey).facadeForSdk val sdkFacade = GlobalFacade(facadeKey).facadeForSdk
val globalContext = sdkFacade.globalContext.contextWithNewLockAndCompositeExceptionTracker() val globalContext = sdkFacade.globalContext.contextWithNewLockAndCompositeExceptionTracker()
return ProjectResolutionFacade( return ProjectResolutionFacade(
"facadeForScriptDependencies", "dependencies of scripts", "facadeForScriptDependencies", "dependencies of scripts",
project, globalContext, facadeKey, project, globalContext, facadeKey,
reuseDataFrom = sdkFacade, reuseDataFrom = sdkFacade,
allModules = dependenciesModuleInfo.dependencies(), allModules = dependenciesModuleInfo.dependencies(),
//TODO: provide correct trackers //TODO: provide correct trackers
dependencies = listOf( dependencies = listOf(
LibraryModificationTracker.getInstance(project), LibraryModificationTracker.getInstance(project),
ProjectRootModificationTracker.getInstance(project), ProjectRootModificationTracker.getInstance(project),
ScriptDependenciesModificationTracker.getInstance(project) ScriptDependenciesModificationTracker.getInstance(project)
), ),
moduleFilter = { it == dependenciesModuleInfo }, moduleFilter = { it == dependenciesModuleInfo },
syntheticFiles = syntheticFiles syntheticFiles = syntheticFiles
) )
} }
@@ -118,51 +118,51 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
private inner class GlobalFacade(settings: PlatformAnalysisSettings) { private inner class GlobalFacade(settings: PlatformAnalysisSettings) {
private val sdkContext = GlobalContext() private val sdkContext = GlobalContext()
val facadeForSdk = ProjectResolutionFacade( val facadeForSdk = ProjectResolutionFacade(
"facadeForSdk", "sdk ${settings.sdk}", "facadeForSdk", "sdk ${settings.sdk}",
project, sdkContext, settings, project, sdkContext, settings,
moduleFilter = { it is SdkInfo }, moduleFilter = { it is SdkInfo },
dependencies = listOf( dependencies = listOf(
LibraryModificationTracker.getInstance(project), LibraryModificationTracker.getInstance(project),
ProjectRootModificationTracker.getInstance(project) ProjectRootModificationTracker.getInstance(project)
), ),
invalidateOnOOCB = false, invalidateOnOOCB = false,
reuseDataFrom = null reuseDataFrom = null
) )
private val librariesContext = sdkContext.contextWithNewLockAndCompositeExceptionTracker() private val librariesContext = sdkContext.contextWithNewLockAndCompositeExceptionTracker()
val facadeForLibraries = ProjectResolutionFacade( val facadeForLibraries = ProjectResolutionFacade(
"facadeForLibraries", "project libraries for platform ${settings.sdk}", "facadeForLibraries", "project libraries for platform ${settings.sdk}",
project, librariesContext, settings, project, librariesContext, settings,
reuseDataFrom = facadeForSdk, reuseDataFrom = facadeForSdk,
moduleFilter = { it is LibraryInfo }, moduleFilter = { it is LibraryInfo },
invalidateOnOOCB = false, invalidateOnOOCB = false,
dependencies = listOf( dependencies = listOf(
LibraryModificationTracker.getInstance(project), LibraryModificationTracker.getInstance(project),
ProjectRootModificationTracker.getInstance(project) ProjectRootModificationTracker.getInstance(project)
) )
) )
private val modulesContext = librariesContext.contextWithNewLockAndCompositeExceptionTracker() private val modulesContext = librariesContext.contextWithNewLockAndCompositeExceptionTracker()
val facadeForModules = ProjectResolutionFacade( val facadeForModules = ProjectResolutionFacade(
"facadeForModules", "project source roots and libraries for platform ${settings.platform}", "facadeForModules", "project source roots and libraries for platform ${settings.platform}",
project, modulesContext, settings, project, modulesContext, settings,
reuseDataFrom = facadeForLibraries, reuseDataFrom = facadeForLibraries,
moduleFilter = { !it.isLibraryClasses() }, moduleFilter = { !it.isLibraryClasses() },
dependencies = listOf( dependencies = listOf(
LibraryModificationTracker.getInstance(project), LibraryModificationTracker.getInstance(project),
ProjectRootModificationTracker.getInstance(project) ProjectRootModificationTracker.getInstance(project)
) )
) )
} }
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 globalFacade(settings: PlatformAnalysisSettings) = private fun globalFacade(settings: PlatformAnalysisSettings) =
getOrBuildGlobalFacade(settings).facadeForModules getOrBuildGlobalFacade(settings).facadeForModules
private fun librariesFacade(settings: PlatformAnalysisSettings) = getOrBuildGlobalFacade(settings).facadeForLibraries private fun librariesFacade(settings: PlatformAnalysisSettings) = getOrBuildGlobalFacade(settings).facadeForLibraries
@@ -183,38 +183,40 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
ModificationTracker { ModificationTracker {
files.sumByLong { it.outOfBlockModificationCount } files.sumByLong { it.outOfBlockModificationCount }
} }
} } else {
else {
ModificationTracker { ModificationTracker {
files.sumByLong { it.outOfBlockModificationCount + it.modificationStamp } files.sumByLong { it.outOfBlockModificationCount + it.modificationStamp }
} }
} }
val dependenciesForSyntheticFileCache = listOf( val dependenciesForSyntheticFileCache =
listOf(
PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT,
filesModificationTracker, filesModificationTracker,
ScriptDependenciesModificationTracker.getInstance(project) ScriptDependenciesModificationTracker.getInstance(project)
) )
val resolverDebugName = "completion/highlighting in $syntheticFileModule for files ${files.joinToString { it.name }} for platform $targetPlatform" val resolverDebugName =
"completion/highlighting in $syntheticFileModule for files ${files.joinToString { it.name }} for platform $targetPlatform"
fun makeProjectResolutionFacade(debugName: String, fun makeProjectResolutionFacade(
globalContext: GlobalContextImpl, debugName: String,
reuseDataFrom: ProjectResolutionFacade? = null, globalContext: GlobalContextImpl,
moduleFilter: (IdeaModuleInfo) -> Boolean = { true }, reuseDataFrom: ProjectResolutionFacade? = null,
allModules: Collection<IdeaModuleInfo>? = null moduleFilter: (IdeaModuleInfo) -> Boolean = { true },
allModules: Collection<IdeaModuleInfo>? = null
): ProjectResolutionFacade { ): ProjectResolutionFacade {
return ProjectResolutionFacade( return ProjectResolutionFacade(
debugName, debugName,
resolverDebugName, resolverDebugName,
project, project,
globalContext, globalContext,
settings, settings,
syntheticFiles = files, syntheticFiles = files,
reuseDataFrom = reuseDataFrom, reuseDataFrom = reuseDataFrom,
moduleFilter = moduleFilter, moduleFilter = moduleFilter,
dependencies = dependenciesForSyntheticFileCache, dependencies = dependenciesForSyntheticFileCache,
allModules = allModules allModules = allModules
) )
} }
@@ -224,10 +226,10 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
val modulesFacade = globalFacade(settings) val modulesFacade = globalFacade(settings)
val globalContext = modulesFacade.globalContext.contextWithNewLockAndCompositeExceptionTracker() val globalContext = modulesFacade.globalContext.contextWithNewLockAndCompositeExceptionTracker()
makeProjectResolutionFacade( makeProjectResolutionFacade(
"facadeForSynthetic in ModuleSourceInfo", "facadeForSynthetic in ModuleSourceInfo",
globalContext, globalContext,
reuseDataFrom = modulesFacade, reuseDataFrom = modulesFacade,
moduleFilter = { it in dependentModules } moduleFilter = { it in dependentModules }
) )
} }
@@ -235,11 +237,11 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
val facadeForScriptDependencies = getFacadeForScriptDependencies(syntheticFileModule) val facadeForScriptDependencies = getFacadeForScriptDependencies(syntheticFileModule)
val globalContext = facadeForScriptDependencies.globalContext.contextWithNewLockAndCompositeExceptionTracker() val globalContext = facadeForScriptDependencies.globalContext.contextWithNewLockAndCompositeExceptionTracker()
makeProjectResolutionFacade( makeProjectResolutionFacade(
"facadeForSynthetic in ScriptModuleInfo", "facadeForSynthetic in ScriptModuleInfo",
globalContext, globalContext,
reuseDataFrom = facadeForScriptDependencies, reuseDataFrom = facadeForScriptDependencies,
allModules = syntheticFileModule.dependencies(), allModules = syntheticFileModule.dependencies(),
moduleFilter = { it == syntheticFileModule } moduleFilter = { it == syntheticFileModule }
) )
} }
syntheticFileModule is ScriptDependenciesModuleInfo -> { syntheticFileModule is ScriptDependenciesModuleInfo -> {
@@ -250,11 +252,11 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
val facadeForScriptDependencies = createFacadeForScriptDependencies(syntheticFileModule.binariesModuleInfo, files) val facadeForScriptDependencies = createFacadeForScriptDependencies(syntheticFileModule.binariesModuleInfo, files)
val globalContext = facadeForScriptDependencies.globalContext.contextWithNewLockAndCompositeExceptionTracker() val globalContext = facadeForScriptDependencies.globalContext.contextWithNewLockAndCompositeExceptionTracker()
makeProjectResolutionFacade( makeProjectResolutionFacade(
"facadeForSynthetic in ScriptDependenciesSourceModuleInfo", "facadeForSynthetic in ScriptDependenciesSourceModuleInfo",
globalContext, globalContext,
reuseDataFrom = facadeForScriptDependencies, reuseDataFrom = facadeForScriptDependencies,
allModules = syntheticFileModule.dependencies(), allModules = syntheticFileModule.dependencies(),
moduleFilter = { it == syntheticFileModule } moduleFilter = { it == syntheticFileModule }
) )
} }
@@ -262,10 +264,10 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
val librariesFacade = librariesFacade(settings) val librariesFacade = librariesFacade(settings)
val globalContext = librariesFacade.globalContext.contextWithNewLockAndCompositeExceptionTracker() val globalContext = librariesFacade.globalContext.contextWithNewLockAndCompositeExceptionTracker()
makeProjectResolutionFacade( makeProjectResolutionFacade(
"facadeForSynthetic in LibrarySourceInfo or NotUnderContentRootModuleInfo", "facadeForSynthetic in LibrarySourceInfo or NotUnderContentRootModuleInfo",
globalContext, globalContext,
reuseDataFrom = librariesFacade, reuseDataFrom = librariesFacade,
moduleFilter = { it == syntheticFileModule } moduleFilter = { it == syntheticFileModule }
) )
} }
@@ -276,8 +278,8 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
LOG.warn("Creating cache with synthetic files ($files) in classes of library $syntheticFileModule") LOG.warn("Creating cache with synthetic files ($files) in classes of library $syntheticFileModule")
val globalContext = GlobalContext() val globalContext = GlobalContext()
makeProjectResolutionFacade( makeProjectResolutionFacade(
"facadeForSynthetic for file under both classes and root", "facadeForSynthetic for file under both classes and root",
globalContext globalContext
) )
} }
@@ -286,33 +288,54 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
} }
private val suppressAnnotationShortName = KotlinBuiltIns.FQ_NAMES.suppress.shortName().identifier private val suppressAnnotationShortName = KotlinBuiltIns.FQ_NAMES.suppress.shortName().identifier
private val kotlinSuppressCache: CachedValue<KotlinSuppressCache> = CachedValuesManager.getManager(project).createCachedValue({ private val kotlinSuppressCache: CachedValue<KotlinSuppressCache> = CachedValuesManager.getManager(project).createCachedValue(
CachedValueProvider.Result<KotlinSuppressCache>(object : KotlinSuppressCache() { {
override fun getSuppressionAnnotations(annotated: KtAnnotated): List<AnnotationDescriptor> { CachedValueProvider.Result<KotlinSuppressCache>(
if (annotated.annotationEntries.none { object : KotlinSuppressCache() {
it.calleeExpression?.text?.endsWith(suppressAnnotationShortName) ?: false }) { override fun getSuppressionAnnotations(annotated: KtAnnotated): List<AnnotationDescriptor> {
// Avoid running resolve heuristics if (annotated.annotationEntries.none {
// TODO: Check aliases in imports it.calleeExpression?.text?.endsWith(suppressAnnotationShortName) == true
return emptyList() }
} ) {
// Avoid running resolve heuristics
// TODO: Check aliases in imports
return emptyList()
}
val context = when (annotated) { val context =
is KtFile -> annotated.fileAnnotationList?.analyze(BodyResolveMode.PARTIAL) ?: return emptyList() when (annotated) {
is KtModifierListOwner -> annotated.modifierList?.analyze(BodyResolveMode.PARTIAL) ?: return emptyList() is KtFile -> {
else -> annotated.analyze(BodyResolveMode.PARTIAL) annotated.fileAnnotationList?.analyze(BodyResolveMode.PARTIAL)
} ?: return emptyList()
}
is KtModifierListOwner -> {
annotated.modifierList?.analyze(BodyResolveMode.PARTIAL)
?: return emptyList()
}
else ->
annotated.analyze(BodyResolveMode.PARTIAL)
}
val annotatedDescriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, annotated) val annotatedDescriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, annotated)
return if (annotatedDescriptor != null) { if (annotatedDescriptor != null) {
annotatedDescriptor.annotations.toList() return annotatedDescriptor.annotations.toList()
} }
else {
annotated.annotationEntries.mapNotNull { context.get(BindingContext.ANNOTATION, it) } return annotated.annotationEntries.mapNotNull {
} context.get(
} BindingContext.ANNOTATION,
}, LibraryModificationTracker.getInstance(project), PsiModificationTracker.MODIFICATION_COUNT) it
}, false) )
}
}
},
LibraryModificationTracker.getInstance(project),
PsiModificationTracker.MODIFICATION_COUNT
)
},
false
)
private val syntheticFileCachesLock = Any() private val syntheticFileCachesLock = Any()
@@ -342,8 +365,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
return if (notInSourceFiles.isNotEmpty()) { return if (notInSourceFiles.isNotEmpty()) {
val projectFacade = getFacadeForSyntheticFiles(notInSourceFiles) val projectFacade = getFacadeForSyntheticFiles(notInSourceFiles)
ResolutionFacadeImpl(projectFacade, moduleInfo) ResolutionFacadeImpl(projectFacade, moduleInfo)
} } else {
else {
val platform = TargetPlatformDetector.getPlatform(file) val platform = TargetPlatformDetector.getPlatform(file)
getResolutionFacadeByModuleInfo(moduleInfo, platform) getResolutionFacadeByModuleInfo(moduleInfo, platform)
} }
@@ -363,7 +385,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
} }
override fun getResolutionFacadeByModuleInfo(moduleInfo: ModuleInfo, platform: TargetPlatform): ResolutionFacade? = override fun getResolutionFacadeByModuleInfo(moduleInfo: ModuleInfo, platform: TargetPlatform): ResolutionFacade? =
(moduleInfo as? IdeaModuleInfo)?.let { getResolutionFacadeByModuleInfo(it, platform) } (moduleInfo as? IdeaModuleInfo)?.let { getResolutionFacadeByModuleInfo(it, platform) }
private fun Collection<KtFile>.filterNotInProjectSource(moduleInfo: IdeaModuleInfo) = mapNotNull { private fun Collection<KtFile>.filterNotInProjectSource(moduleInfo: IdeaModuleInfo) = mapNotNull {
if (it is KtCodeFragment) it.getContextFile() else it if (it is KtCodeFragment) it.getContextFile() else it
@@ -374,7 +396,7 @@ class KotlinCacheServiceImpl(val project: Project) : KotlinCacheService {
private fun KtCodeFragment.getContextFile(): KtFile? { private fun KtCodeFragment.getContextFile(): KtFile? {
val contextElement = context ?: return null val contextElement = context ?: return null
val contextFile = (contextElement as? KtElement)?.containingKtFile val contextFile = (contextElement as? KtElement)?.containingKtFile
?: throw AssertionError("Analyzing kotlin code fragment of type ${this::class.java} with java context of type ${contextElement::class.java}") ?: throw AssertionError("Analyzing kotlin code fragment of type ${this::class.java} with java context of type ${contextElement::class.java}")
return if (contextFile is KtCodeFragment) contextFile.getContextFile() else contextFile return if (contextFile is KtCodeFragment) contextFile.getContextFile() else contextFile
} }
} }
@@ -68,7 +68,7 @@ internal class PerFileAnalysisCache(val file: KtFile, val componentProvider: Com
} }
fun getAnalysisResults(element: KtElement): AnalysisResult { fun getAnalysisResults(element: KtElement): AnalysisResult {
assert (element.containingKtFile == file) { "Wrong file. Expected $file, but was ${element.containingKtFile}" } assert(element.containingKtFile == file) { "Wrong file. Expected $file, but was ${element.containingKtFile}" }
val analyzableParent = KotlinResolveDataProvider.findAnalyzableParent(element) val analyzableParent = KotlinResolveDataProvider.findAnalyzableParent(element)
@@ -93,14 +93,11 @@ internal class PerFileAnalysisCache(val file: KtFile, val componentProvider: Com
try { try {
return KotlinResolveDataProvider.analyze(project, componentProvider, analyzableElement) return KotlinResolveDataProvider.analyze(project, componentProvider, analyzableElement)
} } catch (e: ProcessCanceledException) {
catch (e: ProcessCanceledException) {
throw e throw e
} } catch (e: IndexNotReadyException) {
catch (e: IndexNotReadyException) {
throw e throw e
} } catch (e: Throwable) {
catch (e: Throwable) {
DiagnosticUtils.throwIfRunningOnServer(e) DiagnosticUtils.throwIfRunningOnServer(e)
LOG.error(e) LOG.error(e)
@@ -111,19 +108,19 @@ internal class PerFileAnalysisCache(val file: KtFile, val componentProvider: Com
private object KotlinResolveDataProvider { private object KotlinResolveDataProvider {
private val topmostElementTypes = arrayOf<Class<out PsiElement?>?>( private val topmostElementTypes = arrayOf<Class<out PsiElement?>?>(
KtNamedFunction::class.java, KtNamedFunction::class.java,
KtAnonymousInitializer::class.java, KtAnonymousInitializer::class.java,
KtProperty::class.java, KtProperty::class.java,
KtImportDirective::class.java, KtImportDirective::class.java,
KtPackageDirective::class.java, KtPackageDirective::class.java,
KtCodeFragment::class.java, KtCodeFragment::class.java,
// TODO: Non-analyzable so far, add more granular analysis // TODO: Non-analyzable so far, add more granular analysis
KtAnnotationEntry::class.java, KtAnnotationEntry::class.java,
KtTypeConstraint::class.java, KtTypeConstraint::class.java,
KtSuperTypeList::class.java, KtSuperTypeList::class.java,
KtTypeParameter::class.java, KtTypeParameter::class.java,
KtParameter::class.java, KtParameter::class.java,
KtTypeAlias::class.java KtTypeAlias::class.java
) )
fun findAnalyzableParent(element: KtElement): KtElement { fun findAnalyzableParent(element: KtElement): KtElement {
@@ -145,10 +142,10 @@ private object KotlinResolveDataProvider {
// Class initializer should be replaced by containing class to provide full analysis // Class initializer should be replaced by containing class to provide full analysis
if (analyzableElement is KtClassInitializer) return analyzableElement.containingDeclaration if (analyzableElement is KtClassInitializer) return analyzableElement.containingDeclaration
return analyzableElement return analyzableElement
// if none of the above worked, take the outermost declaration // if none of the above worked, take the outermost declaration
?: PsiTreeUtil.getTopmostParentOfType(element, KtDeclaration::class.java) ?: PsiTreeUtil.getTopmostParentOfType(element, KtDeclaration::class.java)
// if even that didn't work, take the whole file // if even that didn't work, take the whole file
?: element.containingKtFile ?: element.containingKtFile
} }
fun analyze(project: Project, componentProvider: ComponentProvider, analyzableElement: KtElement): AnalysisResult { fun analyze(project: Project, componentProvider: ComponentProvider, analyzableElement: KtElement): AnalysisResult {
@@ -159,32 +156,33 @@ private object KotlinResolveDataProvider {
} }
val resolveSession = componentProvider.get<ResolveSession>() val resolveSession = componentProvider.get<ResolveSession>()
val trace = DelegatingBindingTrace(resolveSession.bindingContext, "Trace for resolution of " + analyzableElement, allowSliceRewrite = true) val trace = DelegatingBindingTrace(
resolveSession.bindingContext,
"Trace for resolution of " + analyzableElement,
allowSliceRewrite = true
)
val targetPlatform = TargetPlatformDetector.getPlatform(analyzableElement.containingKtFile) val targetPlatform = TargetPlatformDetector.getPlatform(analyzableElement.containingKtFile)
val lazyTopDownAnalyzer = createContainerForLazyBodyResolve( val lazyTopDownAnalyzer = createContainerForLazyBodyResolve(
//TODO: should get ModuleContext //TODO: should get ModuleContext
componentProvider.get<GlobalContext>().withProject(project).withModule(module), componentProvider.get<GlobalContext>().withProject(project).withModule(module),
resolveSession, resolveSession,
trace, trace,
targetPlatform, targetPlatform,
componentProvider.get<BodyResolveCache>(), componentProvider.get<BodyResolveCache>(),
analyzableElement.jvmTarget, analyzableElement.jvmTarget,
analyzableElement.languageVersionSettings analyzableElement.languageVersionSettings
).get<LazyTopDownAnalyzer>() ).get<LazyTopDownAnalyzer>()
lazyTopDownAnalyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, listOf(analyzableElement)) lazyTopDownAnalyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, listOf(analyzableElement))
return AnalysisResult.success(trace.bindingContext, module) return AnalysisResult.success(trace.bindingContext, module)
} } catch (e: ProcessCanceledException) {
catch (e: ProcessCanceledException) {
throw e throw e
} } catch (e: IndexNotReadyException) {
catch (e: IndexNotReadyException) {
throw e throw e
} } catch (e: Throwable) {
catch (e: Throwable) {
DiagnosticUtils.throwIfRunningOnServer(e) DiagnosticUtils.throwIfRunningOnServer(e)
LOG.error(e) LOG.error(e)
@@ -195,9 +193,9 @@ private object KotlinResolveDataProvider {
private fun analyzeExpressionCodeFragment(componentProvider: ComponentProvider, codeFragment: KtCodeFragment): BindingContext { private fun analyzeExpressionCodeFragment(componentProvider: ComponentProvider, codeFragment: KtCodeFragment): BindingContext {
val trace = BindingTraceContext() val trace = BindingTraceContext()
componentProvider.get<CodeFragmentAnalyzer>().analyzeCodeFragment( componentProvider.get<CodeFragmentAnalyzer>().analyzeCodeFragment(
codeFragment, codeFragment,
trace, trace,
BodyResolveMode.PARTIAL_FOR_COMPLETION //TODO: discuss it BodyResolveMode.PARTIAL_FOR_COMPLETION //TODO: discuss it
) )
return trace.bindingContext return trace.bindingContext
} }
@@ -48,12 +48,14 @@ interface LibraryDependenciesCache {
class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDependenciesCache { class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDependenciesCache {
val cache by CachedValue(project) { val cache by CachedValue(project) {
CachedValueProvider.Result(ContainerUtil.createConcurrentWeakMap<Library, LibrariesAndSdks>(), CachedValueProvider.Result(
ProjectRootManager.getInstance(project)) ContainerUtil.createConcurrentWeakMap<Library, LibrariesAndSdks>(),
ProjectRootManager.getInstance(project)
)
} }
override fun getLibrariesAndSdksUsedWith(library: Library): LibrariesAndSdks = override fun getLibrariesAndSdksUsedWith(library: Library): LibrariesAndSdks =
cache.getOrPut(library) { computeLibrariesAndSdksUsedWith(library) } cache.getOrPut(library) { computeLibrariesAndSdksUsedWith(library) }
//NOTE: used LibraryRuntimeClasspathScope as reference //NOTE: used LibraryRuntimeClasspathScope as reference
@@ -63,8 +65,7 @@ class LibraryDependenciesCacheImpl(private val project: Project) : LibraryDepend
if (orderEntry is ModuleOrderEntry) { if (orderEntry is ModuleOrderEntry) {
val module = orderEntry.module val module = orderEntry.module
module != null && module !in processedModules module != null && module !in processedModules
} } else {
else {
true true
} }
} }
@@ -109,4 +109,5 @@ class LibraryModificationTracker(project: Project) : SimpleModificationTracker()
} }
} }
private fun isRelevantEvent(vFileEvent: VFileEvent) = vFileEvent is VFileCreateEvent || vFileEvent is VFileMoveEvent || vFileEvent is VFileCopyEvent private fun isRelevantEvent(vFileEvent: VFileEvent) =
vFileEvent is VFileCreateEvent || vFileEvent is VFileMoveEvent || vFileEvent is VFileCopyEvent
@@ -44,7 +44,7 @@ object MapPsiToAsmDesc {
is PsiTypeParameter -> resolved.superTypes.firstOrNull()?.let { typeDesc(it) } ?: "Ljava/lang/Object;" is PsiTypeParameter -> resolved.superTypes.firstOrNull()?.let { typeDesc(it) } ?: "Ljava/lang/Object;"
is PsiClass -> classDesc(resolved) is PsiClass -> classDesc(resolved)
null -> unknownSignature() null -> unknownSignature()
else -> error("Resolved to unexpected $resolved of class ${resolved::class.java}" ) else -> error("Resolved to unexpected $resolved of class ${resolved::class.java}")
} }
} }
@@ -71,7 +71,7 @@ object MapPsiToAsmDesc {
psiMethod.returnType?.let { psiMethod.returnType?.let {
append(typeDesc(it)) append(typeDesc(it))
} }
?: return unknownSignature() // TODO: support constructors, there seems to be additional logic in java that doesn't work correctly for compiled kotlin ?: return unknownSignature() // TODO: support constructors, there seems to be additional logic in java that doesn't work correctly for compiled kotlin
} }
private fun unknownSignature() = "" private fun unknownSignature() = ""
@@ -79,6 +79,7 @@ object MapPsiToAsmDesc {
LOG.error(message) LOG.error(message)
return unknownSignature() return unknownSignature()
} }
private fun primitive(asmType: Type) = asmType.descriptor private fun primitive(asmType: Type) = asmType.descriptor
private val LOG = Logger.getInstance(this::class.java) private val LOG = Logger.getInstance(this::class.java)
@@ -42,17 +42,17 @@ import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters import org.jetbrains.kotlin.resolve.jvm.JvmPlatformParameters
fun createModuleResolverProvider( fun createModuleResolverProvider(
debugName: String, debugName: String,
project: Project, project: Project,
globalContext: GlobalContextImpl, globalContext: GlobalContextImpl,
analysisSettings: PlatformAnalysisSettings, analysisSettings: PlatformAnalysisSettings,
syntheticFiles: Collection<KtFile>, syntheticFiles: Collection<KtFile>,
delegateResolver: ResolverForProject<IdeaModuleInfo>, delegateResolver: ResolverForProject<IdeaModuleInfo>,
moduleFilter: (IdeaModuleInfo) -> Boolean, moduleFilter: (IdeaModuleInfo) -> Boolean,
allModules: Collection<IdeaModuleInfo>?, allModules: Collection<IdeaModuleInfo>?,
providedBuiltIns: KotlinBuiltIns?, // null means create new builtins based on SDK providedBuiltIns: KotlinBuiltIns?, // null means create new builtins based on SDK
dependencies: Collection<Any>, dependencies: Collection<Any>,
invalidateOnOOCB: Boolean = true invalidateOnOOCB: Boolean = true
): ModuleResolverProvider { ): ModuleResolverProvider {
val builtIns = providedBuiltIns ?: createBuiltIns(analysisSettings, globalContext) val builtIns = providedBuiltIns ?: createBuiltIns(analysisSettings, globalContext)
@@ -74,19 +74,19 @@ fun createModuleResolverProvider(
} }
val resolverForProject = ResolverForProjectImpl( val resolverForProject = ResolverForProjectImpl(
debugName, globalContext.withProject(project), modulesToCreateResolversFor, debugName, globalContext.withProject(project), modulesToCreateResolversFor,
{ module -> { module ->
val platform = module.platform ?: analysisSettings.platform val platform = module.platform ?: analysisSettings.platform
IdePlatformSupport.facades[platform] ?: throw UnsupportedOperationException("Unsupported platform $platform") IdePlatformSupport.facades[platform] ?: throw UnsupportedOperationException("Unsupported platform $platform")
}, },
modulesContent, jvmPlatformParameters, modulesContent, jvmPlatformParameters,
IdeaEnvironment, builtIns, IdeaEnvironment, builtIns,
delegateResolver, { _, c -> IDEPackagePartProvider(c.moduleContentScope) }, delegateResolver, { _, c -> IDEPackagePartProvider(c.moduleContentScope) },
analysisSettings.sdk?.let { SdkInfo(project, it) }, analysisSettings.sdk?.let { SdkInfo(project, it) },
modulePlatforms = { module -> module.platform?.multiTargetPlatform }, modulePlatforms = { module -> module.platform?.multiTargetPlatform },
packageOracleFactory = ServiceManager.getService(project, IdePackageOracleFactory::class.java), packageOracleFactory = ServiceManager.getService(project, IdePackageOracleFactory::class.java),
languageSettingsProvider = IDELanguageSettingsProvider, languageSettingsProvider = IDELanguageSettingsProvider,
invalidateOnOOCB = invalidateOnOOCB invalidateOnOOCB = invalidateOnOOCB
) )
if (providedBuiltIns == null && builtIns is JvmBuiltIns) { if (providedBuiltIns == null && builtIns is JvmBuiltIns) {
@@ -95,9 +95,9 @@ fun createModuleResolverProvider(
} }
return ModuleResolverProvider( return ModuleResolverProvider(
resolverForProject, resolverForProject,
builtIns, builtIns,
dependencies + listOf(globalContext.exceptionTracker) dependencies + listOf(globalContext.exceptionTracker)
) )
} }
@@ -136,7 +136,7 @@ fun getAllProjectSdks(): Collection<Sdk> {
class ModuleResolverProvider( class ModuleResolverProvider(
val resolverForProject: ResolverForProject<IdeaModuleInfo>, val resolverForProject: ResolverForProject<IdeaModuleInfo>,
val builtIns: KotlinBuiltIns, val builtIns: KotlinBuiltIns,
val cacheDependencies: Collection<Any> val cacheDependencies: Collection<Any>
) )
@@ -33,40 +33,40 @@ import org.jetbrains.kotlin.resolve.CompositeBindingContext
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
internal class ProjectResolutionFacade( internal class ProjectResolutionFacade(
private val debugString: String, private val debugString: String,
private val resolverDebugName: String, private val resolverDebugName: String,
val project: Project, val project: Project,
val globalContext: GlobalContextImpl, val globalContext: GlobalContextImpl,
val settings: PlatformAnalysisSettings, val settings: PlatformAnalysisSettings,
val reuseDataFrom: ProjectResolutionFacade?, val reuseDataFrom: ProjectResolutionFacade?,
val moduleFilter: (IdeaModuleInfo) -> Boolean, val moduleFilter: (IdeaModuleInfo) -> Boolean,
val dependencies: List<Any>, val dependencies: List<Any>,
private val invalidateOnOOCB: Boolean = true, private val invalidateOnOOCB: Boolean = true,
val syntheticFiles: Collection<KtFile> = listOf(), val syntheticFiles: Collection<KtFile> = listOf(),
val allModules: Collection<IdeaModuleInfo>? = null // null means create resolvers for modules from idea model val allModules: Collection<IdeaModuleInfo>? = null // null means create resolvers for modules from idea model
) { ) {
private val cachedValue = CachedValuesManager.getManager(project).createCachedValue( private val cachedValue = CachedValuesManager.getManager(project).createCachedValue(
{ {
val resolverProvider = computeModuleResolverProvider() val resolverProvider = computeModuleResolverProvider()
CachedValueProvider.Result.create(resolverProvider, resolverProvider.cacheDependencies) CachedValueProvider.Result.create(resolverProvider, resolverProvider.cacheDependencies)
}, },
/* trackValue = */ false /* trackValue = */ false
) )
private fun computeModuleResolverProvider(): ModuleResolverProvider { private fun computeModuleResolverProvider(): ModuleResolverProvider {
val delegateResolverProvider = reuseDataFrom?.moduleResolverProvider val delegateResolverProvider = reuseDataFrom?.moduleResolverProvider
val delegateResolverForProject = delegateResolverProvider?.resolverForProject ?: EmptyResolverForProject() val delegateResolverForProject = delegateResolverProvider?.resolverForProject ?: EmptyResolverForProject()
return createModuleResolverProvider( return createModuleResolverProvider(
resolverDebugName, resolverDebugName,
project, project,
globalContext, globalContext,
settings, settings,
syntheticFiles = syntheticFiles, syntheticFiles = syntheticFiles,
delegateResolver = delegateResolverForProject, moduleFilter = moduleFilter, delegateResolver = delegateResolverForProject, moduleFilter = moduleFilter,
allModules = allModules, allModules = allModules,
providedBuiltIns = delegateResolverProvider?.builtIns, providedBuiltIns = delegateResolverProvider?.builtIns,
dependencies = dependencies, dependencies = dependencies,
invalidateOnOOCB = invalidateOnOOCB invalidateOnOOCB = invalidateOnOOCB
) )
} }
@@ -91,17 +91,21 @@ internal class ProjectResolutionFacade(
} }
private val analysisResults = CachedValuesManager.getManager(project).createCachedValue( private val analysisResults = CachedValuesManager.getManager(project).createCachedValue(
{ {
val resolverProvider = moduleResolverProvider val resolverProvider = moduleResolverProvider
val results = object : SLRUCache<KtFile, PerFileAnalysisCache>(2, 3) { val results = object : SLRUCache<KtFile, PerFileAnalysisCache>(2, 3) {
override fun createValue(file: KtFile): PerFileAnalysisCache { override fun createValue(file: KtFile): PerFileAnalysisCache {
return PerFileAnalysisCache(file, resolverProvider.resolverForProject.resolverForModule(file.getModuleInfo()).componentProvider) return PerFileAnalysisCache(
} file,
resolverProvider.resolverForProject.resolverForModule(file.getModuleInfo()).componentProvider
)
} }
}
val allDependencies = resolverProvider.cacheDependencies + listOf(PsiModificationTracker.MODIFICATION_COUNT) val allDependencies = resolverProvider.cacheDependencies + listOf(PsiModificationTracker.MODIFICATION_COUNT)
CachedValueProvider.Result.create(results, allDependencies) CachedValueProvider.Result.create(results, allDependencies)
}, false) }, false
)
fun getAnalysisResultsForElements(elements: Collection<KtElement>): AnalysisResult { fun getAnalysisResultsForElements(elements: Collection<KtElement>): AnalysisResult {
assert(elements.isNotEmpty()) { "elements collection should not be empty" } assert(elements.isNotEmpty()) { "elements collection should not be empty" }
@@ -36,8 +36,8 @@ import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
internal class ResolutionFacadeImpl( internal class ResolutionFacadeImpl(
private val projectFacade: ProjectResolutionFacade, private val projectFacade: ProjectResolutionFacade,
private val moduleInfo: IdeaModuleInfo private val moduleInfo: IdeaModuleInfo
) : ResolutionFacade { ) : ResolutionFacade {
override val project: Project override val project: Project
get() = projectFacade.project get() = projectFacade.project
@@ -59,22 +59,21 @@ internal class ResolutionFacadeImpl(
return resolveElementCache.resolveToElements(elements, bodyResolveMode) return resolveElementCache.resolveToElements(elements, bodyResolveMode)
} }
override fun analyzeFullyAndGetResult(elements: Collection<KtElement>): AnalysisResult override fun analyzeFullyAndGetResult(elements: Collection<KtElement>): AnalysisResult =
= projectFacade.getAnalysisResultsForElements(elements) projectFacade.getAnalysisResultsForElements(elements)
override fun resolveToDescriptor(declaration: KtDeclaration, bodyResolveMode: BodyResolveMode): DeclarationDescriptor { override fun resolveToDescriptor(declaration: KtDeclaration, bodyResolveMode: BodyResolveMode): DeclarationDescriptor {
return if (KtPsiUtil.isLocal(declaration)) { return if (KtPsiUtil.isLocal(declaration)) {
val bindingContext = analyze(declaration, bodyResolveMode) val bindingContext = analyze(declaration, bodyResolveMode)
bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration]
?: getFrontendService(moduleInfo, AbsentDescriptorHandler::class.java).diagnoseDescriptorNotFound(declaration) ?: getFrontendService(moduleInfo, AbsentDescriptorHandler::class.java).diagnoseDescriptorNotFound(declaration)
} } else {
else {
val resolveSession = projectFacade.resolverForElement(declaration).componentProvider.get<ResolveSession>() val resolveSession = projectFacade.resolverForElement(declaration).componentProvider.get<ResolveSession>()
resolveSession.resolveToDescriptor(declaration) resolveSession.resolveToDescriptor(declaration)
} }
} }
override fun <T : Any> getFrontendService(serviceClass: Class<T>): T = getFrontendService(moduleInfo, serviceClass) override fun <T : Any> getFrontendService(serviceClass: Class<T>): T = getFrontendService(moduleInfo, serviceClass)
override fun <T : Any> getIdeService(serviceClass: Class<T>): T { override fun <T : Any> getIdeService(serviceClass: Class<T>): T {
return projectFacade.resolverForModuleInfo(moduleInfo).componentProvider.create(serviceClass) return projectFacade.resolverForModuleInfo(moduleInfo).componentProvider.create(serviceClass)
@@ -38,9 +38,9 @@ class ScriptModuleSearchScope(val scriptFile: VirtualFile, baseScope: GlobalSear
} }
data class ScriptModuleInfo( data class ScriptModuleInfo(
val project: Project, val project: Project,
val scriptFile: VirtualFile, val scriptFile: VirtualFile,
val scriptDefinition: KotlinScriptDefinition val scriptDefinition: KotlinScriptDefinition
) : IdeaModuleInfo { ) : IdeaModuleInfo {
override val moduleOrigin: ModuleOrigin override val moduleOrigin: ModuleOrigin
get() = ModuleOrigin.OTHER get() = ModuleOrigin.OTHER
@@ -54,27 +54,30 @@ data class ScriptModuleInfo(
override fun dependencies(): List<IdeaModuleInfo> { override fun dependencies(): List<IdeaModuleInfo> {
return listOf( return listOf(
this, ScriptDependenciesModuleInfo(project, this) this, ScriptDependenciesModuleInfo(project, this)
) + sdkDependencies(externalDependencies, project) ) + sdkDependencies(externalDependencies, project)
} }
} }
private fun sdkDependencies(scriptDependencies: ScriptDependencies?, project: Project): List<SdkInfo> private fun sdkDependencies(scriptDependencies: ScriptDependencies?, project: Project): List<SdkInfo> =
= listOfNotNull(findJdk(scriptDependencies, project)?.let { SdkInfo(project, it) }) listOfNotNull(findJdk(scriptDependencies, project)?.let { SdkInfo(project, it) })
fun findJdk(dependencies: ScriptDependencies?, project: Project): Sdk? { fun findJdk(dependencies: ScriptDependencies?, project: Project): Sdk? {
val allJdks = getAllProjectSdks() val allJdks = getAllProjectSdks()
// workaround for mismatched gradle wrapper and plugin version // workaround for mismatched gradle wrapper and plugin version
val javaHome = try { dependencies?.javaHome?.canonicalPath } catch (e: Throwable) { null } val javaHome = try {
dependencies?.javaHome?.canonicalPath
} catch (e: Throwable) {
null
}
return allJdks.find { javaHome != null && it.homePath == javaHome } ?: return allJdks.find { javaHome != null && it.homePath == javaHome } ?: ProjectRootManager.getInstance(project).projectSdk
ProjectRootManager.getInstance(project).projectSdk ?: ?: allJdks.firstOrNull()
allJdks.firstOrNull()
} }
class ScriptDependenciesModuleInfo( class ScriptDependenciesModuleInfo(
val project: Project, val project: Project,
val scriptModuleInfo: ScriptModuleInfo? val scriptModuleInfo: ScriptModuleInfo?
) : IdeaModuleInfo, BinaryModuleInfo { ) : IdeaModuleInfo, BinaryModuleInfo {
override fun dependencies() = (listOf(this) + sdkDependencies(scriptModuleInfo?.externalDependencies, project)) override fun dependencies() = (listOf(this) + sdkDependencies(scriptModuleInfo?.externalDependencies, project))
@@ -84,7 +87,7 @@ class ScriptDependenciesModuleInfo(
if (scriptModuleInfo == null) { if (scriptModuleInfo == null) {
// we do not know which scripts these dependencies are // we do not know which scripts these dependencies are
return KotlinSourceFilterScope.libraryClassFiles( return KotlinSourceFilterScope.libraryClassFiles(
ScriptDependenciesManager.getInstance(project).getAllScriptsClasspathScope(), project ScriptDependenciesManager.getInstance(project).getAllScriptsClasspathScope(), project
) )
} }
return ServiceManager.getService(project, ScriptBinariesScopeCache::class.java).get(scriptModuleInfo.externalDependencies) return ServiceManager.getService(project, ScriptBinariesScopeCache::class.java).get(scriptModuleInfo.externalDependencies)
@@ -93,6 +96,7 @@ class ScriptDependenciesModuleInfo(
// NOTE: intentionally not taking corresponding script info into account // NOTE: intentionally not taking corresponding script info into account
// otherwise there is no way to implement getModuleInfo // otherwise there is no way to implement getModuleInfo
override fun hashCode() = project.hashCode() override fun hashCode() = project.hashCode()
override fun equals(other: Any?): Boolean = other is ScriptDependenciesModuleInfo && this.project == other.project override fun equals(other: Any?): Boolean = other is ScriptDependenciesModuleInfo && this.project == other.project
override val moduleOrigin: ModuleOrigin override val moduleOrigin: ModuleOrigin
@@ -103,7 +107,7 @@ class ScriptDependenciesModuleInfo(
} }
data class ScriptDependenciesSourceModuleInfo( data class ScriptDependenciesSourceModuleInfo(
val project: Project val project: Project
) : IdeaModuleInfo, SourceForBinaryModuleInfo { ) : IdeaModuleInfo, SourceForBinaryModuleInfo {
override val name = Name.special("<Source for script dependencies>") override val name = Name.special("<Source for script dependencies>")
@@ -111,7 +115,7 @@ data class ScriptDependenciesSourceModuleInfo(
get() = ScriptDependenciesModuleInfo(project, null) get() = ScriptDependenciesModuleInfo(project, null)
override fun sourceScope(): GlobalSearchScope = KotlinSourceFilterScope.librarySources( override fun sourceScope(): GlobalSearchScope = KotlinSourceFilterScope.librarySources(
ScriptDependenciesManager.getInstance(project).getAllLibrarySourcesScope(), project ScriptDependenciesManager.getInstance(project).getAllLibrarySourcesScope(), project
) )
} }
@@ -43,43 +43,44 @@ fun PsiElement.getNullableModuleInfo(): IdeaModuleInfo? = this.collectInfos(Modu
fun PsiElement.getModuleInfos(): Sequence<IdeaModuleInfo> = this.collectInfos(ModuleInfoCollector.ToSequence) fun PsiElement.getModuleInfos(): Sequence<IdeaModuleInfo> = this.collectInfos(ModuleInfoCollector.ToSequence)
fun getModuleInfoByVirtualFile(project: Project, virtualFile: VirtualFile): IdeaModuleInfo? = collectInfosByVirtualFile( fun getModuleInfoByVirtualFile(project: Project, virtualFile: VirtualFile): IdeaModuleInfo? = collectInfosByVirtualFile(
project, virtualFile, project, virtualFile,
treatAsLibrarySource = false, treatAsLibrarySource = false,
onOccurrence = { return@getModuleInfoByVirtualFile it } onOccurrence = { return@getModuleInfoByVirtualFile it }
) )
fun getBinaryLibrariesModuleInfos(project: Project, virtualFile: VirtualFile) fun getBinaryLibrariesModuleInfos(project: Project, virtualFile: VirtualFile) =
= collectModuleInfosByType<BinaryModuleInfo>(project, virtualFile) collectModuleInfosByType<BinaryModuleInfo>(project, virtualFile)
fun getLibrarySourcesModuleInfos(project: Project, virtualFile: VirtualFile)
= collectModuleInfosByType<LibrarySourceInfo>(project, virtualFile) fun getLibrarySourcesModuleInfos(project: Project, virtualFile: VirtualFile) =
collectModuleInfosByType<LibrarySourceInfo>(project, virtualFile)
private typealias VirtualFileProcessor<T> = (Project, VirtualFile, Boolean) -> T private typealias VirtualFileProcessor<T> = (Project, VirtualFile, Boolean) -> T
private sealed class ModuleInfoCollector<out T>( private sealed class ModuleInfoCollector<out T>(
val onResult: (IdeaModuleInfo?) -> T, val onResult: (IdeaModuleInfo?) -> T,
val onFailure: (String) -> T, val onFailure: (String) -> T,
val virtualFileProcessor: VirtualFileProcessor<T> val virtualFileProcessor: VirtualFileProcessor<T>
) { ) {
object NotNullTakeFirst : ModuleInfoCollector<IdeaModuleInfo>( object NotNullTakeFirst : ModuleInfoCollector<IdeaModuleInfo>(
onResult = { it ?: NotUnderContentRootModuleInfo }, onResult = { it ?: NotUnderContentRootModuleInfo },
onFailure = { reason -> onFailure = { reason ->
LOG.error("Could not find correct module information.\nReason: $reason") LOG.error("Could not find correct module information.\nReason: $reason")
NotUnderContentRootModuleInfo NotUnderContentRootModuleInfo
}, },
virtualFileProcessor = processor@ { project, virtualFile, isLibrarySource -> virtualFileProcessor = processor@ { project, virtualFile, isLibrarySource ->
collectInfosByVirtualFile(project, virtualFile, isLibrarySource, { return@processor it ?: NotUnderContentRootModuleInfo }) collectInfosByVirtualFile(project, virtualFile, isLibrarySource, { return@processor it ?: NotUnderContentRootModuleInfo })
} }
) )
object NullableTakeFirst: ModuleInfoCollector<IdeaModuleInfo?>( object NullableTakeFirst : ModuleInfoCollector<IdeaModuleInfo?>(
onResult = { it }, onResult = { it },
onFailure = { reason -> onFailure = { reason ->
LOG.warn("Could not find correct module information.\nReason: $reason") LOG.warn("Could not find correct module information.\nReason: $reason")
null null
}, },
virtualFileProcessor = processor@ { project, virtualFile, isLibrarySource -> virtualFileProcessor = processor@ { project, virtualFile, isLibrarySource ->
collectInfosByVirtualFile(project, virtualFile, isLibrarySource, { return@processor it }) collectInfosByVirtualFile(project, virtualFile, isLibrarySource, { return@processor it })
} }
) )
object ToSequence : ModuleInfoCollector<Sequence<IdeaModuleInfo>>( object ToSequence : ModuleInfoCollector<Sequence<IdeaModuleInfo>>(
@@ -105,8 +106,8 @@ private fun <T> PsiElement.collectInfos(c: ModuleInfoCollector<T>): T {
return this.processLightElement(c) return this.processLightElement(c)
} }
val containingFile = containingFile ?: val containingFile =
return c.onFailure("Analyzing element of type ${this::class.java} with no containing file\nText:\n$text") containingFile ?: return c.onFailure("Analyzing element of type ${this::class.java} with no containing file\nText:\n$text")
val containingKtFile = (this as? KtElement)?.containingFile as? KtFile val containingKtFile = (this as? KtElement)?.containingFile as? KtFile
containingKtFile?.analysisContext?.let { containingKtFile?.analysisContext?.let {
@@ -123,18 +124,18 @@ private fun <T> PsiElement.collectInfos(c: ModuleInfoCollector<T>): T {
} }
if (containingKtFile is KtCodeFragment) { if (containingKtFile is KtCodeFragment) {
val context = containingKtFile.getContext() ?: val context = containingKtFile.getContext()
return c.onFailure("Analyzing code fragment of type ${containingKtFile::class.java} with no context element\nText:\n${containingKtFile.getText()}") ?: return c.onFailure("Analyzing code fragment of type ${containingKtFile::class.java} with no context element\nText:\n${containingKtFile.getText()}")
return context.collectInfos(c) return context.collectInfos(c)
} }
val virtualFile = containingFile.originalFile.virtualFile val virtualFile = containingFile.originalFile.virtualFile
?: return c.onFailure("Analyzing element of type ${this::class.java} in non-physical file $containingFile of type ${containingFile::class.java}\nText:\n$text") ?: return c.onFailure("Analyzing element of type ${this::class.java} in non-physical file $containingFile of type ${containingFile::class.java}\nText:\n$text")
return c.virtualFileProcessor( return c.virtualFileProcessor(
project, project,
virtualFile, virtualFile,
(containingFile as? KtFile)?.isCompiled ?: false (containingFile as? KtFile)?.isCompiled ?: false
) )
} }
@@ -142,9 +143,9 @@ private fun <T> KtLightElement<*, *>.processLightElement(c: ModuleInfoCollector<
val decompiledClass = this.getParentOfType<KtLightClassForDecompiledDeclaration>(strict = false) val decompiledClass = this.getParentOfType<KtLightClassForDecompiledDeclaration>(strict = false)
if (decompiledClass != null) { if (decompiledClass != null) {
return c.virtualFileProcessor( return c.virtualFileProcessor(
project, project,
containingFile.virtualFile.sure { "Decompiled class should be build from physical file" }, containingFile.virtualFile.sure { "Decompiled class should be build from physical file" },
false false
) )
} }
@@ -158,8 +159,8 @@ private fun <T> KtLightElement<*, *>.processLightElement(c: ModuleInfoCollector<
} }
private inline fun <T> collectInfosByVirtualFile( private inline fun <T> collectInfosByVirtualFile(
project: Project, virtualFile: VirtualFile, project: Project, virtualFile: VirtualFile,
treatAsLibrarySource: Boolean, onOccurrence: (IdeaModuleInfo?) -> T treatAsLibrarySource: Boolean, onOccurrence: (IdeaModuleInfo?) -> T
): T { ): T {
val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project) val projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project)
@@ -168,8 +169,7 @@ private inline fun <T> collectInfosByVirtualFile(
val moduleFileIndex = ModuleRootManager.getInstance(module).fileIndex val moduleFileIndex = ModuleRootManager.getInstance(module).fileIndex
if (moduleFileIndex.isInTestSourceContent(virtualFile)) { if (moduleFileIndex.isInTestSourceContent(virtualFile)) {
onOccurrence(module.testSourceInfo()) onOccurrence(module.testSourceInfo())
} } else if (moduleFileIndex.isInSourceContentWithoutInjected(virtualFile)) {
else if (moduleFileIndex.isInSourceContentWithoutInjected(virtualFile)) {
onOccurrence(module.productionSourceInfo()) onOccurrence(module.productionSourceInfo())
} }
} }
@@ -188,8 +188,7 @@ private inline fun <T> collectInfosByVirtualFile(
if (isBinary && virtualFile in scriptConfigurationManager.getAllScriptsClasspathScope()) { if (isBinary && virtualFile in scriptConfigurationManager.getAllScriptsClasspathScope()) {
if (treatAsLibrarySource) { if (treatAsLibrarySource) {
onOccurrence(ScriptDependenciesSourceModuleInfo(project)) onOccurrence(ScriptDependenciesSourceModuleInfo(project))
} } else {
else {
onOccurrence(ScriptDependenciesModuleInfo(project, null)) onOccurrence(ScriptDependenciesModuleInfo(project, null))
} }
} }
@@ -210,9 +209,9 @@ private inline fun <reified T : IdeaModuleInfo> collectModuleInfosByType(project
} }
private fun OrderEntry.toIdeaModuleInfo( private fun OrderEntry.toIdeaModuleInfo(
project: Project, project: Project,
virtualFile: VirtualFile, virtualFile: VirtualFile,
treatAsLibrarySource: Boolean = false treatAsLibrarySource: Boolean = false
): IdeaModuleInfo? { ): IdeaModuleInfo? {
if (this is ModuleOrderEntry) return null if (this is ModuleOrderEntry) return null
if (!isValid) return null if (!isValid) return null
@@ -222,8 +221,7 @@ private fun OrderEntry.toIdeaModuleInfo(
val library = library ?: return null val library = library ?: return null
if (ProjectRootsUtil.isLibraryClassFile(project, virtualFile) && !treatAsLibrarySource) { if (ProjectRootsUtil.isLibraryClassFile(project, virtualFile) && !treatAsLibrarySource) {
return LibraryInfo(project, library) return LibraryInfo(project, library)
} } else if (ProjectRootsUtil.isLibraryFile(project, virtualFile) || treatAsLibrarySource) {
else if (ProjectRootsUtil.isLibraryFile(project, virtualFile) || treatAsLibrarySource) {
return LibrarySourceInfo(project, library) return LibrarySourceInfo(project, library)
} }
} }
@@ -49,11 +49,9 @@ class ClsJavaStubByVirtualFileCache {
try { try {
return ClsFileImpl.buildFileStub(file, file.contentsToByteArray(false)) return ClsFileImpl.buildFileStub(file, file.contentsToByteArray(false))
} } catch (e: ClsFormatException) {
catch (e: ClsFormatException) {
LOG.error("Failed to build java cls class for " + file.canonicalPath!!, e) LOG.error("Failed to build java cls class for " + file.canonicalPath!!, e)
} } catch (e: IOException) {
catch (e: IOException) {
LOG.error("Failed to build java cls class for " + file.canonicalPath!!, e) LOG.error("Failed to build java cls class for " + file.canonicalPath!!, e)
} }
@@ -75,8 +75,8 @@ import org.jetbrains.kotlin.types.WrappedTypeFactory
import org.jetbrains.kotlin.utils.sure import org.jetbrains.kotlin.utils.sure
class IDELightClassConstructionContext(bindingContext: BindingContext, module: ModuleDescriptor, val mode: Mode) class IDELightClassConstructionContext(bindingContext: BindingContext, module: ModuleDescriptor, val mode: Mode) :
: LightClassConstructionContext(bindingContext, module) { LightClassConstructionContext(bindingContext, module) {
enum class Mode { enum class Mode {
LIGHT, LIGHT,
EXACT EXACT
@@ -95,9 +95,8 @@ object IDELightClassContexts {
// need to make sure default values for parameters are resolved // need to make sure default values for parameters are resolved
// because java resolve depends on whether there is a default value for an annotation attribute // because java resolve depends on whether there is a default value for an annotation attribute
resolutionFacade.getFrontendService(ResolveElementCache::class.java) resolutionFacade.getFrontendService(ResolveElementCache::class.java)
.resolvePrimaryConstructorParametersDefaultValues(classOrObject) .resolvePrimaryConstructorParametersDefaultValues(classOrObject)
} } else {
else {
resolutionFacade.analyze(classOrObject) resolutionFacade.analyze(classOrObject)
} }
val classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject).sure { val classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject).sure {
@@ -149,7 +148,11 @@ object IDELightClassContexts {
fun lightContextForClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext? { fun lightContextForClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext? {
if (!isDummyResolveApplicable(classOrObject)) return null if (!isDummyResolveApplicable(classOrObject)) return null
val resolveSession = setupAdHocResolve(classOrObject.project, classOrObject.getResolutionFacade().moduleDescriptor, listOf(classOrObject.containingKtFile)) val resolveSession = setupAdHocResolve(
classOrObject.project,
classOrObject.getResolutionFacade().moduleDescriptor,
listOf(classOrObject.containingKtFile)
)
ForceResolveUtil.forceResolveAllContents(resolveSession.resolveToDescriptor(classOrObject)) ForceResolveUtil.forceResolveAllContents(resolveSession.resolveToDescriptor(classOrObject))
@@ -167,9 +170,10 @@ object IDELightClassContexts {
fun lightContextForScript(script: KtScript): LightClassConstructionContext { fun lightContextForScript(script: KtScript): LightClassConstructionContext {
val resolveSession = setupAdHocResolve( val resolveSession = setupAdHocResolve(
script.project, script.project,
script.getResolutionFacade().moduleDescriptor, script.getResolutionFacade().moduleDescriptor,
listOf(script.containingKtFile)) listOf(script.containingKtFile)
)
ForceResolveUtil.forceResolveAllContents(resolveSession.resolveToDescriptor(script)) ForceResolveUtil.forceResolveAllContents(resolveSession.resolveToDescriptor(script))
@@ -188,22 +192,23 @@ object IDELightClassContexts {
return classOrObject.declarations.filterIsInstance<KtClassOrObject>().all { isDummyResolveApplicable(it) } return classOrObject.declarations.filterIsInstance<KtClassOrObject>().all { isDummyResolveApplicable(it) }
} }
private fun hasDelegatedSupertypes(classOrObject: KtClassOrObject) = classOrObject.superTypeListEntries.any { it is KtDelegatedSuperTypeEntry } private fun hasDelegatedSupertypes(classOrObject: KtClassOrObject) =
classOrObject.superTypeListEntries.any { it is KtDelegatedSuperTypeEntry }
private fun isDataClassWithGeneratedMembersOverridden(classOrObject: KtClassOrObject): Boolean { private fun isDataClassWithGeneratedMembersOverridden(classOrObject: KtClassOrObject): Boolean {
return classOrObject.hasModifier(KtTokens.DATA_KEYWORD) && return classOrObject.hasModifier(KtTokens.DATA_KEYWORD) &&
classOrObject.declarations.filterIsInstance<KtFunction>().any { classOrObject.declarations.filterIsInstance<KtFunction>().any {
isGeneratedForDataClass(it.nameAsSafeName) isGeneratedForDataClass(it.nameAsSafeName)
} }
} }
private fun isGeneratedForDataClass(name: Name): Boolean { private fun isGeneratedForDataClass(name: Name): Boolean {
return name == DataClassDescriptorResolver.EQUALS_METHOD_NAME || return name == DataClassDescriptorResolver.EQUALS_METHOD_NAME ||
// known failure is related to equals override, checking for other methods 'just in case' // known failure is related to equals override, checking for other methods 'just in case'
name == DataClassDescriptorResolver.COPY_METHOD_NAME || name == DataClassDescriptorResolver.COPY_METHOD_NAME ||
name == DataClassDescriptorResolver.HASH_CODE_METHOD_NAME || name == DataClassDescriptorResolver.HASH_CODE_METHOD_NAME ||
name == DataClassDescriptorResolver.TO_STRING_METHOD_NAME || name == DataClassDescriptorResolver.TO_STRING_METHOD_NAME ||
DataClassDescriptorResolver.isComponentLike(name) DataClassDescriptorResolver.isComponentLike(name)
} }
private fun hasMembersOverridingInternalMembers(classOrObject: KtClassOrObject): Boolean { private fun hasMembersOverridingInternalMembers(classOrObject: KtClassOrObject): Boolean {
@@ -221,8 +226,8 @@ object IDELightClassContexts {
private fun anyInternalMembersWithThisName(name: String, project: Project): Boolean { private fun anyInternalMembersWithThisName(name: String, project: Project): Boolean {
var result = false var result = false
StubIndex.getInstance().processElements( StubIndex.getInstance().processElements(
KotlinOverridableInternalMembersShortNameIndex.Instance.key, name, project, KotlinOverridableInternalMembersShortNameIndex.Instance.key, name, project,
EverythingGlobalScope(project), KtCallableDeclaration::class.java EverythingGlobalScope(project), KtCallableDeclaration::class.java
) { ) {
result = true result = true
false // stop processing at first matching result false // stop processing at first matching result
@@ -284,8 +289,8 @@ object IDELightClassContexts {
val container = createContainer("LightClassStub", JvmPlatform) { val container = createContainer("LightClassStub", JvmPlatform) {
val jvmTarget = IDELanguageSettingsProvider.getTargetPlatform(moduleInfo) as? JvmTarget val jvmTarget = IDELanguageSettingsProvider.getTargetPlatform(moduleInfo) as? JvmTarget
configureModule( configureModule(
ModuleContext(moduleDescriptor, project), JvmPlatform, ModuleContext(moduleDescriptor, project), JvmPlatform,
jvmTarget ?: JvmTarget.DEFAULT, trace jvmTarget ?: JvmTarget.DEFAULT, trace
) )
useInstance(GlobalSearchScope.EMPTY_SCOPE) useInstance(GlobalSearchScope.EMPTY_SCOPE)
@@ -321,27 +326,26 @@ object IDELightClassContexts {
fun get(name: String): ClassDescriptor? { fun get(name: String): ClassDescriptor? {
val annotationFqName = annotationsThatAffectCodegen.firstOrNull { it.shortName().asString() == name } ?: return null val annotationFqName = annotationsThatAffectCodegen.firstOrNull { it.shortName().asString() == name } ?: return null
return realModule.getPackage(annotationFqName.parent()).memberScope return realModule.getPackage(annotationFqName.parent()).memberScope
.getContributedClassifier(annotationFqName.shortName(), NoLookupLocation.FROM_IDE) as? ClassDescriptor .getContributedClassifier(annotationFqName.shortName(), NoLookupLocation.FROM_IDE) as? ClassDescriptor
} }
// see JvmPlatformAnnotations.kt, JvmFlagAnnotations.kt, also PsiModifier.MODIFIERS // see JvmPlatformAnnotations.kt, JvmFlagAnnotations.kt, also PsiModifier.MODIFIERS
private val annotationsThatAffectCodegen = listOf( private val annotationsThatAffectCodegen = listOf(
"JvmField", "JvmOverloads", "JvmName", "JvmStatic", "JvmField", "JvmOverloads", "JvmName", "JvmStatic",
"Synchronized", "Transient", "Volatile", "Strictfp" "Synchronized", "Transient", "Volatile", "Strictfp"
).map { FqName("kotlin.jvm").child(Name.identifier(it)) } + ).map { FqName("kotlin.jvm").child(Name.identifier(it)) } +
FqName("kotlin.PublishedApi") + FqName("kotlin.PublishedApi") +
FqName("kotlin.Deprecated") + FqName("kotlin.Deprecated") +
FqName("kotlin.internal.InlineOnly") + FqName("kotlin.internal.InlineOnly") +
FqName("kotlinx.android.parcel.Parcelize") FqName("kotlinx.android.parcel.Parcelize")
} }
class AdHocAnnotationResolver( class AdHocAnnotationResolver(
private val codegenAffectingAnnotations: CodegenAffectingAnnotations, private val codegenAffectingAnnotations: CodegenAffectingAnnotations,
private val callResolver: CallResolver, private val callResolver: CallResolver,
private val languageVersionSettings: LanguageVersionSettings, private val languageVersionSettings: LanguageVersionSettings,
private val dataFlowValueFactory: DataFlowValueFactory, private val dataFlowValueFactory: DataFlowValueFactory,constantExpressionEvaluator: ConstantExpressionEvaluator,
constantExpressionEvaluator: ConstantExpressionEvaluator, storageManager: StorageManager
storageManager: StorageManager
) : AnnotationResolverImpl(callResolver, constantExpressionEvaluator, storageManager) { ) : AnnotationResolverImpl(callResolver, constantExpressionEvaluator, storageManager) {
override fun resolveAnnotationType(scope: LexicalScope, entryElement: KtAnnotationEntry, trace: BindingTrace): KotlinType { override fun resolveAnnotationType(scope: LexicalScope, entryElement: KtAnnotationEntry, trace: BindingTrace): KotlinType {
@@ -354,20 +358,24 @@ object IDELightClassContexts {
return codegenAffectingAnnotations.get(referencedName) return codegenAffectingAnnotations.get(referencedName)
} }
override fun resolveAnnotationCall(annotationEntry: KtAnnotationEntry, scope: LexicalScope, trace: BindingTrace): OverloadResolutionResults<FunctionDescriptor> { override fun resolveAnnotationCall(
annotationEntry: KtAnnotationEntry,
scope: LexicalScope,
trace: BindingTrace
): OverloadResolutionResults<FunctionDescriptor> {
val annotationConstructor = annotationClassByEntry(annotationEntry)?.constructors?.singleOrNull() val annotationConstructor = annotationClassByEntry(annotationEntry)?.constructors?.singleOrNull()
?: return super.resolveAnnotationCall(annotationEntry, scope, trace) ?: return super.resolveAnnotationCall(annotationEntry, scope, trace)
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return callResolver.resolveConstructorCall( return callResolver.resolveConstructorCall(
BasicCallResolutionContext.create( BasicCallResolutionContext.create(
trace, scope, CallMaker.makeCall(null, null, annotationEntry), TypeUtils.NO_EXPECTED_TYPE, trace, scope, CallMaker.makeCall(null, null, annotationEntry), TypeUtils.NO_EXPECTED_TYPE,
DataFlowInfoFactory.EMPTY, ContextDependency.INDEPENDENT, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS, DataFlowInfoFactory.EMPTY, ContextDependency.INDEPENDENT, CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
true, languageVersionSettings, true, languageVersionSettings,
dataFlowValueFactory dataFlowValueFactory
), ),
annotationEntry.calleeExpression!!.constructorReferenceExpression!!, annotationEntry.calleeExpression!!.constructorReferenceExpression!!,
annotationConstructor.returnType annotationConstructor.returnType
) as OverloadResolutionResults<FunctionDescriptor> ) as OverloadResolutionResults<FunctionDescriptor>
} }
} }
@@ -30,9 +30,9 @@ import org.jetbrains.kotlin.psi.KtSuperTypeListEntry
class IdeLightClassInheritanceHelper : LightClassInheritanceHelper { class IdeLightClassInheritanceHelper : LightClassInheritanceHelper {
override fun isInheritor( override fun isInheritor(
lightClass: KtLightClass, lightClass: KtLightClass,
baseClass: PsiClass, baseClass: PsiClass,
checkDeep: Boolean checkDeep: Boolean
): ImpreciseResolveResult { ): ImpreciseResolveResult {
val classOrObject = lightClass.kotlinOrigin ?: return UNSURE val classOrObject = lightClass.kotlinOrigin ?: return UNSURE
val entries = classOrObject.superTypeListEntries val entries = classOrObject.superTypeListEntries
@@ -36,8 +36,8 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
// Used as a placeholder when actual light class does not exist (expect-classes, for example) // Used as a placeholder when actual light class does not exist (expect-classes, for example)
// The main purpose is to allow search of inheritors within hierarchies containing such classes // The main purpose is to allow search of inheritors within hierarchies containing such classes
class KtFakeLightClass(override val kotlinOrigin: KtClassOrObject) : class KtFakeLightClass(override val kotlinOrigin: KtClassOrObject) :
AbstractLightClass(kotlinOrigin.manager, KotlinLanguage.INSTANCE), AbstractLightClass(kotlinOrigin.manager, KotlinLanguage.INSTANCE),
KtLightClass { KtLightClass {
private val _delegate by lazy { PsiElementFactory.SERVICE.getInstance(kotlinOrigin.project).createClass("dummy") } private val _delegate by lazy { PsiElementFactory.SERVICE.getInstance(kotlinOrigin.project).createClass("dummy") }
private val _containingClass by lazy { kotlinOrigin.containingClassOrObject?.let { KtFakeLightClass(it) } } private val _containingClass by lazy { kotlinOrigin.containingClassOrObject?.let { KtFakeLightClass(it) } }
@@ -62,18 +62,21 @@ class KtFakeLightClass(override val kotlinOrigin: KtClassOrObject) :
val baseKtClass = (baseClass as? KtLightClass)?.kotlinOrigin ?: return false val baseKtClass = (baseClass as? KtLightClass)?.kotlinOrigin ?: return false
val baseDescriptor = baseKtClass.resolveToDescriptorIfAny() ?: return false val baseDescriptor = baseKtClass.resolveToDescriptorIfAny() ?: return false
val thisDescriptor = kotlinOrigin.resolveToDescriptorIfAny() ?: return false val thisDescriptor = kotlinOrigin.resolveToDescriptorIfAny() ?: return false
return if (checkDeep) DescriptorUtils.isSubclass(thisDescriptor, baseDescriptor) else DescriptorUtils.isDirectSubclass(thisDescriptor, baseDescriptor) return if (checkDeep)
DescriptorUtils.isSubclass(thisDescriptor, baseDescriptor)
else
DescriptorUtils.isDirectSubclass(thisDescriptor, baseDescriptor)
} }
} }
class KtFakeLightMethod private constructor( class KtFakeLightMethod private constructor(
val ktDeclaration: KtNamedDeclaration, val ktDeclaration: KtNamedDeclaration,
ktClassOrObject : KtClassOrObject ktClassOrObject: KtClassOrObject
) : LightMethod ( ) : LightMethod(
ktDeclaration.manager, ktDeclaration.manager,
PsiElementFactory.SERVICE.getInstance(ktDeclaration.project).createMethod("dummy", PsiType.VOID), PsiElementFactory.SERVICE.getInstance(ktDeclaration.project).createMethod("dummy", PsiType.VOID),
KtFakeLightClass(ktClassOrObject), KtFakeLightClass(ktClassOrObject),
KotlinLanguage.INSTANCE KotlinLanguage.INSTANCE
), KtLightElement<KtNamedDeclaration, PsiMethod> { ), KtLightElement<KtNamedDeclaration, PsiMethod> {
override val kotlinOrigin get() = ktDeclaration override val kotlinOrigin get() = ktDeclaration
override val clsDelegate get() = myMethod override val clsDelegate get() = myMethod
@@ -29,9 +29,9 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
class KtLightClassForDecompiledDeclaration( class KtLightClassForDecompiledDeclaration(
override val clsDelegate: ClsClassImpl, override val clsDelegate: ClsClassImpl,
override val kotlinOrigin: KtClassOrObject?, override val kotlinOrigin: KtClassOrObject?,
private val file: KtClsFile private val file: KtClsFile
) : KtLightClassBase(clsDelegate.manager) { ) : KtLightClassBase(clsDelegate.manager) {
val fqName = kotlinOrigin?.fqName ?: FqName(clsDelegate.qualifiedName.orEmpty()) val fqName = kotlinOrigin?.fqName ?: FqName(clsDelegate.qualifiedName.orEmpty())
@@ -41,7 +41,8 @@ class KtLightClassForDecompiledDeclaration(
val nestedClasses = kotlinOrigin?.declarations?.filterIsInstance<KtClassOrObject>() ?: emptyList() val nestedClasses = kotlinOrigin?.declarations?.filterIsInstance<KtClassOrObject>() ?: emptyList()
return clsDelegate.ownInnerClasses.map { innerClsClass -> return clsDelegate.ownInnerClasses.map { innerClsClass ->
KtLightClassForDecompiledDeclaration(innerClsClass as ClsClassImpl, KtLightClassForDecompiledDeclaration(innerClsClass as ClsClassImpl,
nestedClasses.firstOrNull { innerClsClass.name == it.name }, file) nestedClasses.firstOrNull { innerClsClass.name == it.name }, file
)
} }
} }
@@ -58,11 +59,11 @@ class KtLightClassForDecompiledDeclaration(
override fun getParent() = clsDelegate.parent override fun getParent() = clsDelegate.parent
override fun equals(other: Any?): Boolean = override fun equals(other: Any?): Boolean =
other is KtLightClassForDecompiledDeclaration && other is KtLightClassForDecompiledDeclaration &&
fqName == other.fqName fqName == other.fqName
override fun hashCode(): Int = override fun hashCode(): Int =
fqName.hashCode() fqName.hashCode()
override val originKind: LightClassOriginKind override val originKind: LightClassOriginKind
get() = LightClassOriginKind.BINARY get() = LightClassOriginKind.BINARY
@@ -31,22 +31,23 @@ typealias ExactLightClassContextProvider = () -> LightClassConstructionContext
typealias DummyLightClassContextProvider = (() -> LightClassConstructionContext?)? typealias DummyLightClassContextProvider = (() -> LightClassConstructionContext?)?
sealed class LazyLightClassDataHolder( sealed class LazyLightClassDataHolder(
builder: LightClassBuilder, builder: LightClassBuilder,
project: Project, project: Project,
exactContextProvider: ExactLightClassContextProvider, exactContextProvider: ExactLightClassContextProvider,
dummyContextProvider: DummyLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider,
isLocal: Boolean = false isLocal: Boolean = false
) : LightClassDataHolder { ) : LightClassDataHolder {
private val exactResultCachedValue = private val exactResultCachedValue =
CachedValuesManager.getManager(project).createCachedValue({ CachedValuesManager.getManager(project).createCachedValue({
CachedValueProvider.Result.create( CachedValueProvider.Result.create(
builder(exactContextProvider()), builder(exactContextProvider()),
if (isLocal) if (isLocal)
PsiModificationTracker.MODIFICATION_COUNT PsiModificationTracker.MODIFICATION_COUNT
else else
PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
}, false) )
}, false)
private val lazyInexactStub by lazyPub { private val lazyInexactStub by lazyPub {
dummyContextProvider?.let { provider -> provider()?.let { context -> builder.invoke(context).stub } } dummyContextProvider?.let { provider -> provider()?.let { context -> builder.invoke(context).stub } }
@@ -60,32 +61,32 @@ sealed class LazyLightClassDataHolder(
// for facade or defaultImpls // for facade or defaultImpls
override fun findData(findDelegate: (PsiJavaFileStub) -> PsiClass): LightClassData = override fun findData(findDelegate: (PsiJavaFileStub) -> PsiClass): LightClassData =
LazyLightClassData { stub -> LazyLightClassData { stub ->
findDelegate(stub) findDelegate(stub)
} }
class ForClass( class ForClass(
builder: LightClassBuilder, project: Project, isLocal: Boolean, builder: LightClassBuilder, project: Project, isLocal: Boolean,
exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider
) : LazyLightClassDataHolder(builder, project, exactContextProvider, dummyContextProvider, isLocal), LightClassDataHolder.ForClass { ) : LazyLightClassDataHolder(builder, project, exactContextProvider, dummyContextProvider, isLocal), LightClassDataHolder.ForClass {
override fun findDataForClassOrObject(classOrObject: KtClassOrObject): LightClassData = override fun findDataForClassOrObject(classOrObject: KtClassOrObject): LightClassData =
LazyLightClassData { stub -> LazyLightClassData { stub ->
stub.findDelegate(classOrObject) stub.findDelegate(classOrObject)
} }
} }
class ForFacade( class ForFacade(
builder: LightClassBuilder, project: Project, builder: LightClassBuilder, project: Project,
exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider
) : LazyLightClassDataHolder(builder, project, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForFacade ) : LazyLightClassDataHolder(builder, project, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForFacade
class ForScript( class ForScript(
builder: LightClassBuilder, project: Project, builder: LightClassBuilder, project: Project,
exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider exactContextProvider: ExactLightClassContextProvider, dummyContextProvider: DummyLightClassContextProvider
) : LazyLightClassDataHolder(builder, project, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForScript ) : LazyLightClassDataHolder(builder, project, exactContextProvider, dummyContextProvider), LightClassDataHolder.ForScript
private inner class LazyLightClassData( private inner class LazyLightClassData(
findDelegate: (PsiJavaFileStub) -> PsiClass findDelegate: (PsiJavaFileStub) -> PsiClass
) : LightClassData { ) : LightClassData {
override val clsDelegate: PsiClass by lazyPub { findDelegate(javaFileStub) } override val clsDelegate: PsiClass by lazyPub { findDelegate(javaFileStub) }
@@ -121,7 +122,7 @@ sealed class LazyLightClassDataHolder(
Resulting light member is not consistent in this case, so this should happen only for erroneous code Resulting light member is not consistent in this case, so this should happen only for erroneous code
*/ */
val exactDelegateMethod = clsDelegate.findMethodsByName(dummyMethod.name, false).firstOrNull(byMemberIndex) val exactDelegateMethod = clsDelegate.findMethodsByName(dummyMethod.name, false).firstOrNull(byMemberIndex)
?: clsDelegate.methods.firstOrNull(byMemberIndex) ?: clsDelegate.methods.firstOrNull(byMemberIndex)
exactDelegateMethod.assertMatches(dummyMethod, containingClass) exactDelegateMethod.assertMatches(dummyMethod, containingClass)
} }
} }
@@ -141,8 +142,8 @@ sealed class LazyLightClassDataHolder(
} }
} }
private sealed class LazyLightClassMemberMatchingError(message: String, containingClass: KtLightClass) private sealed class LazyLightClassMemberMatchingError(message: String, containingClass: KtLightClass) :
: KotlinExceptionWithAttachments(message) { KotlinExceptionWithAttachments(message) {
init { init {
containingClass.kotlinOrigin?.hasLightClassMatchingErrors = true containingClass.kotlinOrigin?.hasLightClassMatchingErrors = true
@@ -150,11 +151,11 @@ private sealed class LazyLightClassMemberMatchingError(message: String, containi
} }
class NoMatch(dummyMember: PsiMember, containingClass: KtLightClass) : LazyLightClassMemberMatchingError( class NoMatch(dummyMember: PsiMember, containingClass: KtLightClass) : LazyLightClassMemberMatchingError(
"Couldn't match ${dummyMember.debugName}", containingClass "Couldn't match ${dummyMember.debugName}", containingClass
) )
class WrongMatch(realMember: PsiMember, dummyMember: PsiMember, containingClass: KtLightClass) : LazyLightClassMemberMatchingError( class WrongMatch(realMember: PsiMember, dummyMember: PsiMember, containingClass: KtLightClass) : LazyLightClassMemberMatchingError(
"Matched ${dummyMember.debugName} to ${realMember.debugName}", containingClass "Matched ${dummyMember.debugName} to ${realMember.debugName}", containingClass
) )
} }
@@ -43,35 +43,31 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.psi.UserDataProperty import org.jetbrains.kotlin.psi.UserDataProperty
private val readOnlyQualifiedNamesToJavaClass = JavaToKotlinClassMap.mutabilityMappings.associateBy { private val readOnlyQualifiedNamesToJavaClass = JavaToKotlinClassMap.mutabilityMappings.associateBy { (_, readOnly, _) ->
(_, readOnly, _) ->
readOnly.asSingleFqName() readOnly.asSingleFqName()
} }
private val mutableQualifiedNamesToJavaClass = JavaToKotlinClassMap.mutabilityMappings.associateBy { private val mutableQualifiedNamesToJavaClass = JavaToKotlinClassMap.mutabilityMappings.associateBy { (_, _, mutable) ->
(_, _, mutable) ->
mutable.asSingleFqName() mutable.asSingleFqName()
} }
private val membersWithSpecializedSignature: Set<String> = private val membersWithSpecializedSignature: Set<String> =
BuiltinMethodsWithSpecialGenericSignature.ERASED_VALUE_PARAMETERS_SIGNATURES.mapTo(LinkedHashSet()) { BuiltinMethodsWithSpecialGenericSignature.ERASED_VALUE_PARAMETERS_SIGNATURES.mapTo(LinkedHashSet()) {
val fqNameString = it.substringBefore('(').replace('/', '.') val fqNameString = it.substringBefore('(').replace('/', '.')
FqName(fqNameString).shortName().asString() FqName(fqNameString).shortName().asString()
} }
private val javaGetterNameToKotlinGetterName: Map<String, String> = BuiltinSpecialProperties.PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP.map { private val javaGetterNameToKotlinGetterName: Map<String, String> =
(propertyFqName, javaGetterShortName) -> BuiltinSpecialProperties.PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP.map { (propertyFqName, javaGetterShortName) ->
Pair(javaGetterShortName.asString(), JvmAbi.getterName(propertyFqName.shortName().asString())) Pair(javaGetterShortName.asString(), JvmAbi.getterName(propertyFqName.shortName().asString()))
}.toMap() }.toMap()
fun platformMutabilityWrapper(fqName: FqName, findJavaClass: (String) -> PsiClass?): PsiClass? { fun platformMutabilityWrapper(fqName: FqName, findJavaClass: (String) -> PsiClass?): PsiClass? {
readOnlyQualifiedNamesToJavaClass[fqName]?.let { readOnlyQualifiedNamesToJavaClass[fqName]?.let { (javaClass, kotlinReadOnly) ->
(javaClass, kotlinReadOnly) ->
val javaBaseClass = findJavaClass(javaClass.asSingleFqName().asString()) ?: return null val javaBaseClass = findJavaClass(javaClass.asSingleFqName().asString()) ?: return null
return getOrCreateWrapper(javaBaseClass, kotlinReadOnly.asSingleFqName(), isMutable = false) return getOrCreateWrapper(javaBaseClass, kotlinReadOnly.asSingleFqName(), isMutable = false)
} }
mutableQualifiedNamesToJavaClass[fqName]?.let { mutableQualifiedNamesToJavaClass[fqName]?.let { (javaClass, _, kotlinMutable) ->
(javaClass, _, kotlinMutable) ->
val javaBaseClass = findJavaClass(javaClass.asSingleFqName().asString()) ?: return null val javaBaseClass = findJavaClass(javaClass.asSingleFqName().asString()) ?: return null
return getOrCreateWrapper(javaBaseClass, kotlinMutable.asSingleFqName(), isMutable = true) return getOrCreateWrapper(javaBaseClass, kotlinMutable.asSingleFqName(), isMutable = true)
} }
@@ -80,16 +76,20 @@ fun platformMutabilityWrapper(fqName: FqName, findJavaClass: (String) -> PsiClas
private fun getOrCreateWrapper(javaBaseClass: PsiClass, kotlinFqName: FqName, isMutable: Boolean): KtLightMutabilityPlatformWrapper { private fun getOrCreateWrapper(javaBaseClass: PsiClass, kotlinFqName: FqName, isMutable: Boolean): KtLightMutabilityPlatformWrapper {
val userDataStorage = if (isMutable) javaBaseClass::mutableWrapper else javaBaseClass::readOnlyWrapper val userDataStorage = if (isMutable) javaBaseClass::mutableWrapper else javaBaseClass::readOnlyWrapper
return userDataStorage.get() ?: KtLightMutabilityPlatformWrapper(javaBaseClass, kotlinFqName, isMutable).also { userDataStorage.set(it) } return userDataStorage.get() ?: KtLightMutabilityPlatformWrapper(
javaBaseClass,
kotlinFqName,
isMutable
).also { userDataStorage.set(it) }
} }
private var PsiClass.readOnlyWrapper: KtLightMutabilityPlatformWrapper? by UserDataProperty(Key.create("READ_ONLY_WRAPPER")) private var PsiClass.readOnlyWrapper: KtLightMutabilityPlatformWrapper? by UserDataProperty(Key.create("READ_ONLY_WRAPPER"))
private var PsiClass.mutableWrapper: KtLightMutabilityPlatformWrapper? by UserDataProperty(Key.create("MUTABLE_WRAPPER")) private var PsiClass.mutableWrapper: KtLightMutabilityPlatformWrapper? by UserDataProperty(Key.create("MUTABLE_WRAPPER"))
class KtLightMutabilityPlatformWrapper( class KtLightMutabilityPlatformWrapper(
private val javaBaseClass: PsiClass, private val javaBaseClass: PsiClass,
private val kotlinInterfaceFqName: FqName, private val kotlinInterfaceFqName: FqName,
private val isMutable: Boolean private val isMutable: Boolean
) : KtAbstractContainerWrapper(kotlinInterfaceFqName, javaBaseClass), PsiClass { ) : KtAbstractContainerWrapper(kotlinInterfaceFqName, javaBaseClass), PsiClass {
private val _methods by lazyPub { calcMethods() } private val _methods by lazyPub { calcMethods() }
@@ -144,11 +144,11 @@ class KtLightMutabilityPlatformWrapper(
private fun createRemoveAt(baseMethod: PsiMethod): PsiMethod { private fun createRemoveAt(baseMethod: PsiMethod): PsiMethod {
return baseMethod.wrap( return baseMethod.wrap(
name = "removeAt", name = "removeAt",
signature = MethodSignature( signature = MethodSignature(
parameterTypes = listOf(PsiType.INT), parameterTypes = listOf(PsiType.INT),
returnType = singleTypeParameterAsType() returnType = singleTypeParameterAsType()
) )
) )
} }
@@ -156,18 +156,18 @@ class KtLightMutabilityPlatformWrapper(
private fun PsiMethod.openBridge() = wrap(makeFinal = false, hasImplementation = true) private fun PsiMethod.openBridge() = wrap(makeFinal = false, hasImplementation = true)
private fun PsiMethod.wrap( private fun PsiMethod.wrap(
makeFinal: Boolean = false, makeFinal: Boolean = false,
hasImplementation: Boolean = false, hasImplementation: Boolean = false,
name: String = this.name, name: String = this.name,
substituteObjectWith: PsiType? = null, substituteObjectWith: PsiType? = null,
signature: MethodSignature? = null signature: MethodSignature? = null
) = KtLightMethodWrapper( ) = KtLightMethodWrapper(
this@KtLightMutabilityPlatformWrapper, this@wrap, this@KtLightMutabilityPlatformWrapper, this@wrap,
isFinal = makeFinal, isFinal = makeFinal,
name = name, name = name,
substituteObjectWith = substituteObjectWith, substituteObjectWith = substituteObjectWith,
providedSignature = signature, providedSignature = signature,
hasImplementation = hasImplementation hasImplementation = hasImplementation
) )
private fun javaUtilMapMethodWithSpecialSignature(method: PsiMethod): KtLightMethodWrapper? { private fun javaUtilMapMethodWithSpecialSignature(method: PsiMethod): KtLightMethodWrapper? {
@@ -176,30 +176,30 @@ class KtLightMutabilityPlatformWrapper(
val signature = when (method.name) { val signature = when (method.name) {
"get" -> MethodSignature( "get" -> MethodSignature(
parameterTypes = listOf(k), parameterTypes = listOf(k),
returnType = v returnType = v
) )
"getOrDefault" -> MethodSignature( "getOrDefault" -> MethodSignature(
parameterTypes = listOf(k, v), parameterTypes = listOf(k, v),
returnType = v returnType = v
) )
"containsKey" -> MethodSignature( "containsKey" -> MethodSignature(
parameterTypes = listOf(k), parameterTypes = listOf(k),
returnType = PsiType.BOOLEAN returnType = PsiType.BOOLEAN
) )
"containsValue" -> MethodSignature( "containsValue" -> MethodSignature(
parameterTypes = listOf(v), parameterTypes = listOf(v),
returnType = PsiType.BOOLEAN returnType = PsiType.BOOLEAN
) )
"remove" -> "remove" ->
when (method.parameterList.parametersCount) { when (method.parameterList.parametersCount) {
1 -> MethodSignature( 1 -> MethodSignature(
parameterTypes = listOf(k), parameterTypes = listOf(k),
returnType = v returnType = v
) )
2 -> MethodSignature( 2 -> MethodSignature(
parameterTypes = listOf(k, v), parameterTypes = listOf(k, v),
returnType = PsiType.BOOLEAN returnType = PsiType.BOOLEAN
) )
else -> null else -> null
} }
@@ -221,7 +221,7 @@ class KtLightMutabilityPlatformWrapper(
val methodName = Name.identifier(name) val methodName = Name.identifier(name)
return scope.getContributedFunctions(methodName, NoLookupLocation.FROM_IDE).isNotEmpty() return scope.getContributedFunctions(methodName, NoLookupLocation.FROM_IDE).isNotEmpty()
|| scope.getContributedVariables(methodName, NoLookupLocation.FROM_IDE).isNotEmpty() || scope.getContributedVariables(methodName, NoLookupLocation.FROM_IDE).isNotEmpty()
} }
override fun getContainingFile() = javaBaseClass.containingFile override fun getContainingFile() = javaBaseClass.containingFile
@@ -230,13 +230,13 @@ class KtLightMutabilityPlatformWrapper(
private data class MethodSignature(val parameterTypes: List<PsiType>, val returnType: PsiType) private data class MethodSignature(val parameterTypes: List<PsiType>, val returnType: PsiType)
private class KtLightMethodWrapper( private class KtLightMethodWrapper(
private val containingClass: KtAbstractContainerWrapper, private val containingClass: KtAbstractContainerWrapper,
private val baseMethod: PsiMethod, private val baseMethod: PsiMethod,
private val name: String, private val name: String,
private val isFinal: Boolean, private val isFinal: Boolean,
private val hasImplementation: Boolean, private val hasImplementation: Boolean,
private val substituteObjectWith: PsiType?, private val substituteObjectWith: PsiType?,
private val providedSignature: MethodSignature? private val providedSignature: MethodSignature?
) : PsiMethod, KtLightElementBase(containingClass) { ) : PsiMethod, KtLightElementBase(containingClass) {
init { init {
@@ -249,8 +249,7 @@ private class KtLightMethodWrapper(
val substituted = containingClass.substitutor.substitute(psiType) val substituted = containingClass.substitutor.substitute(psiType)
return if (TypeUtils.isJavaLangObject(substituted) && substituteObjectWith != null) { return if (TypeUtils.isJavaLangObject(substituted) && substituteObjectWith != null) {
substituteObjectWith substituteObjectWith
} } else {
else {
substituted substituted
} }
} }
@@ -260,20 +259,22 @@ private class KtLightMethodWrapper(
override val kotlinOrigin get() = null override val kotlinOrigin get() = null
override fun hasModifierProperty(name: String) = override fun hasModifierProperty(name: String) =
when (name) { when (name) {
PsiModifier.DEFAULT -> hasImplementation PsiModifier.DEFAULT -> hasImplementation
PsiModifier.ABSTRACT -> !hasImplementation PsiModifier.ABSTRACT -> !hasImplementation
PsiModifier.FINAL -> isFinal PsiModifier.FINAL -> isFinal
else -> baseMethod.hasModifierProperty(name) else -> baseMethod.hasModifierProperty(name)
} }
override fun getParameterList(): PsiParameterList { override fun getParameterList(): PsiParameterList {
return LightParameterListBuilder(manager, KotlinLanguage.INSTANCE).apply { return LightParameterListBuilder(manager, KotlinLanguage.INSTANCE).apply {
baseMethod.parameterList.parameters.forEachIndexed { index, paramFromJava -> baseMethod.parameterList.parameters.forEachIndexed { index, paramFromJava ->
val type = providedSignature?.parameterTypes?.get(index) ?: substituteType(paramFromJava.type) val type = providedSignature?.parameterTypes?.get(index) ?: substituteType(paramFromJava.type)
addParameter( addParameter(
LightParameter(paramFromJava.name ?: "p$index", type, LightParameter(
this@KtLightMethodWrapper, KotlinLanguage.INSTANCE, paramFromJava.isVarArgs) paramFromJava.name ?: "p$index", type,
this@KtLightMethodWrapper, KotlinLanguage.INSTANCE, paramFromJava.isVarArgs
)
) )
} }
} }
@@ -288,8 +289,12 @@ private class KtLightMethodWrapper(
override fun findSuperMethods(checkAccess: Boolean) = PsiSuperMethodImplUtil.findSuperMethods(this, checkAccess) override fun findSuperMethods(checkAccess: Boolean) = PsiSuperMethodImplUtil.findSuperMethods(this, checkAccess)
override fun findSuperMethods(parentClass: PsiClass) = PsiSuperMethodImplUtil.findSuperMethods(this, parentClass) override fun findSuperMethods(parentClass: PsiClass) = PsiSuperMethodImplUtil.findSuperMethods(this, parentClass)
override fun findSuperMethods() = PsiSuperMethodImplUtil.findSuperMethods(this) override fun findSuperMethods() = PsiSuperMethodImplUtil.findSuperMethods(this)
override fun findSuperMethodSignaturesIncludingStatic(checkAccess: Boolean) = PsiSuperMethodImplUtil.findSuperMethodSignaturesIncludingStatic(this, checkAccess) override fun findSuperMethodSignaturesIncludingStatic(checkAccess: Boolean) =
@Suppress("OverridingDeprecatedMember") override fun findDeepestSuperMethod() = PsiSuperMethodImplUtil.findDeepestSuperMethod(this) PsiSuperMethodImplUtil.findSuperMethodSignaturesIncludingStatic(this, checkAccess)
@Suppress("OverridingDeprecatedMember")
override fun findDeepestSuperMethod() = PsiSuperMethodImplUtil.findDeepestSuperMethod(this)
override fun findDeepestSuperMethods() = PsiSuperMethodImplUtil.findDeepestSuperMethods(this) override fun findDeepestSuperMethods() = PsiSuperMethodImplUtil.findDeepestSuperMethods(this)
override fun getHierarchicalMethodSignature() = PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this) override fun getHierarchicalMethodSignature() = PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this)
override fun getSignature(substitutor: PsiSubstitutor) = MethodSignatureBackedByPsiMethod.create(this, substitutor) override fun getSignature(substitutor: PsiSubstitutor) = MethodSignatureBackedByPsiMethod.create(this, substitutor)
@@ -313,17 +318,16 @@ private class KtLightMethodWrapper(
} }
abstract class KtAbstractContainerWrapper(internal val fqName: FqName, private val superInterface: PsiClass) abstract class KtAbstractContainerWrapper(internal val fqName: FqName, private val superInterface: PsiClass) :
: LightElement(superInterface.manager, KotlinLanguage.INSTANCE), PsiExtensibleClass { LightElement(superInterface.manager, KotlinLanguage.INSTANCE), PsiExtensibleClass {
private val memberCache = ClassInnerStuffCache(this) private val memberCache = ClassInnerStuffCache(this)
private val superClassTypeParametersToMyTypeParameters: Map<PsiTypeParameter, PsiTypeParameter> private val superClassTypeParametersToMyTypeParameters: Map<PsiTypeParameter, PsiTypeParameter> = superInterface.typeParameters
= superInterface.typeParameters .mapIndexed { index, supersParameter ->
.mapIndexed { index, supersParameter -> supersParameter to LightTypeParameterBuilder(supersParameter.name ?: "T$index", this, index)
supersParameter to LightTypeParameterBuilder(supersParameter.name ?: "T$index", this, index) }
} .toMap()
.toMap()
internal val substitutor = createSubstitutor(superClassTypeParametersToMyTypeParameters.mapValues { internal val substitutor = createSubstitutor(superClassTypeParametersToMyTypeParameters.mapValues {
it.value.asType() it.value.asType()
@@ -378,14 +382,16 @@ abstract class KtAbstractContainerWrapper(internal val fqName: FqName, private v
override fun getSuperClass() = null override fun getSuperClass() = null
override fun findInnerClassByName(name: String?, checkBases: Boolean) = null override fun findInnerClassByName(name: String?, checkBases: Boolean) = null
override fun getExtendsListTypes() = PsiClassType.EMPTY_ARRAY override fun getExtendsListTypes() = PsiClassType.EMPTY_ARRAY
override fun isInheritorDeep(baseClass: PsiClass, classToByPass: PsiClass?) = InheritanceImplUtil.isInheritorDeep(this, baseClass, classToByPass) override fun isInheritorDeep(baseClass: PsiClass, classToByPass: PsiClass?) =
InheritanceImplUtil.isInheritorDeep(this, baseClass, classToByPass)
override fun isAnnotationType() = false override fun isAnnotationType() = false
override fun findMethodsAndTheirSubstitutorsByName(name: String?, checkBases: Boolean) override fun findMethodsAndTheirSubstitutorsByName(name: String?, checkBases: Boolean) =
= PsiClassImplUtil.findMethodsAndTheirSubstitutorsByName(this, name, checkBases) PsiClassImplUtil.findMethodsAndTheirSubstitutorsByName(this, name, checkBases)
override fun getInnerClasses() = PsiClass.EMPTY_ARRAY override fun getInnerClasses() = PsiClass.EMPTY_ARRAY
override fun findMethodBySignature(patternMethod: PsiMethod, checkBases: Boolean) override fun findMethodBySignature(patternMethod: PsiMethod, checkBases: Boolean) =
= PsiClassImplUtil.findMethodBySignature(this, patternMethod, checkBases) PsiClassImplUtil.findMethodBySignature(this, patternMethod, checkBases)
override fun findFieldByName(name: String?, checkBases: Boolean) = null override fun findFieldByName(name: String?, checkBases: Boolean) = null
override fun getAllFields() = PsiClassImplUtil.getAllFields(this) override fun getAllFields() = PsiClassImplUtil.getAllFields(this)
@@ -394,7 +400,7 @@ abstract class KtAbstractContainerWrapper(internal val fqName: FqName, private v
override fun getAllMethods() = PsiClassImplUtil.getAllMethods(this) override fun getAllMethods() = PsiClassImplUtil.getAllMethods(this)
override fun getOwnFields() = emptyList<PsiField>() override fun getOwnFields() = emptyList<PsiField>()
override fun getAllMethodsAndTheirSubstitutors() = override fun getAllMethodsAndTheirSubstitutors() =
PsiClassImplUtil.getAllWithSubstitutorsByMap<PsiMethod>(this, PsiClassImplUtil.MemberType.METHOD) PsiClassImplUtil.getAllWithSubstitutorsByMap<PsiMethod>(this, PsiClassImplUtil.MemberType.METHOD)
override fun hasTypeParameters() = true override fun hasTypeParameters() = true
override fun getRBrace() = null override fun getRBrace() = null
@@ -410,8 +416,8 @@ abstract class KtAbstractContainerWrapper(internal val fqName: FqName, private v
override fun getConstructors() = PsiMethod.EMPTY_ARRAY override fun getConstructors() = PsiMethod.EMPTY_ARRAY
override fun isDeprecated() = false override fun isDeprecated() = false
override fun setName(name: String) = cannotModify() override fun setName(name: String) = cannotModify()
override fun findMethodsBySignature(patternMethod: PsiMethod, checkBases: Boolean) override fun findMethodsBySignature(patternMethod: PsiMethod, checkBases: Boolean) =
= PsiClassImplUtil.findMethodsBySignature(this, patternMethod, checkBases) PsiClassImplUtil.findMethodsBySignature(this, patternMethod, checkBases)
} }
private fun PsiTypeParameter.asType() = PsiImmediateClassType(this, PsiSubstitutor.EMPTY) private fun PsiTypeParameter.asType() = PsiImmediateClassType(this, PsiSubstitutor.EMPTY)
@@ -34,8 +34,7 @@ fun ModuleSourceInfo.getDependentModules(): Set<ModuleSourceInfo> {
val dependents = getDependents(module) val dependents = getDependents(module)
return if (isTests()) { return if (isTests()) {
dependents.mapNotNullTo(HashSet<ModuleSourceInfo>(), Module::testSourceInfo) dependents.mapNotNullTo(HashSet<ModuleSourceInfo>(), Module::testSourceInfo)
} } else {
else {
dependents.flatMapTo(HashSet<ModuleSourceInfo>()) { it.correspondingModuleInfos() } dependents.flatMapTo(HashSet<ModuleSourceInfo>()) { it.correspondingModuleInfos() }
} }
} }
@@ -64,9 +64,9 @@ class IdePackageOracleFactory(val project: Project) : PackageOracleFactory {
private val kotlinSourceOracle = KotlinSourceFilesOracle(moduleInfo) private val kotlinSourceOracle = KotlinSourceFilesOracle(moduleInfo)
override fun packageExists(fqName: FqName) = override fun packageExists(fqName: FqName) =
javaPackagesOracle.packageExists(fqName) javaPackagesOracle.packageExists(fqName)
|| kotlinSourceOracle.packageExists(fqName) || kotlinSourceOracle.packageExists(fqName)
|| fqName.isSubpackageOf(ANDROID_SYNTHETIC_PACKAGE_PREFIX) || fqName.isSubpackageOf(ANDROID_SYNTHETIC_PACKAGE_PREFIX)
} }
} }
@@ -37,7 +37,7 @@ fun getResolveScope(file: KtFile): GlobalSearchScope {
return when (file.getModuleInfo()) { return when (file.getModuleInfo()) {
is ModuleSourceInfo -> KotlinSourceFilterScope.projectSourceAndClassFiles(file.resolveScope, file.project) is ModuleSourceInfo -> KotlinSourceFilterScope.projectSourceAndClassFiles(file.resolveScope, file.project)
is ScriptModuleInfo -> file.getModuleInfo().dependencies().map { it.contentScope() }.let { GlobalSearchScope.union(it.toTypedArray()) } is ScriptModuleInfo -> file.getModuleInfo().dependencies().map { it.contentScope() }.let { GlobalSearchScope.union(it.toTypedArray()) }
else -> GlobalSearchScope.EMPTY_SCOPE else -> GlobalSearchScope.EMPTY_SCOPE
} }
} }