AA: expose library modules in the project structure
This commit is contained in:
committed by
Ilya Kirillov
parent
dcc349da0d
commit
cbef3faae7
-1
@@ -80,6 +80,5 @@ internal tailrec fun FirDeclaration.ktSymbolOrigin(): KtSymbolOrigin = when (ori
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class InvalidFirDeclarationOriginForSymbol(declaration: FirDeclaration) :
|
class InvalidFirDeclarationOriginForSymbol(declaration: FirDeclaration) :
|
||||||
IllegalStateException("Invalid FirDeclarationOrigin ${declaration.origin::class.simpleName} for ${declaration.render()}")
|
IllegalStateException("Invalid FirDeclarationOrigin ${declaration.origin::class.simpleName} for ${declaration.render()}")
|
||||||
|
|||||||
+5
-1
@@ -11,10 +11,14 @@ import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
|
|||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.test.services.TestServices
|
import org.jetbrains.kotlin.test.services.TestServices
|
||||||
|
|
||||||
class KotlinProjectStructureProviderTestImpl(testServices: TestServices) : ProjectStructureProvider() {
|
class KotlinProjectStructureProviderTestImpl(private val testServices: TestServices) : ProjectStructureProvider() {
|
||||||
private val moduleInfoProvider = testServices.projectModuleProvider
|
private val moduleInfoProvider = testServices.projectModuleProvider
|
||||||
override fun getKtModuleForKtElement(element: PsiElement): KtModule {
|
override fun getKtModuleForKtElement(element: PsiElement): KtModule {
|
||||||
val containingFile = element.containingFile as KtFile
|
val containingFile = element.containingFile as KtFile
|
||||||
return moduleInfoProvider.getModuleInfoByKtFile(containingFile) as KtModule
|
return moduleInfoProvider.getModuleInfoByKtFile(containingFile) as KtModule
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getKtLibraryModules(): Collection<TestKtLibraryModule> {
|
||||||
|
return moduleInfoProvider.getLibraryModules()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -26,6 +26,10 @@ class TestKtModuleProvider(
|
|||||||
|
|
||||||
fun getModule(moduleName: String): TestKtModule =
|
fun getModule(moduleName: String): TestKtModule =
|
||||||
cache.getValue(moduleName)
|
cache.getValue(moduleName)
|
||||||
|
|
||||||
|
fun getLibraryModules(): Collection<TestKtLibraryModule> {
|
||||||
|
return cache.values.filterIsInstance<TestKtLibraryModule>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val TestServices.projectModuleProvider: TestKtModuleProvider by TestServices.testServiceAccessor()
|
val TestServices.projectModuleProvider: TestKtModuleProvider by TestServices.testServiceAccessor()
|
||||||
|
|||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.analysis.providers.impl
|
||||||
|
|
||||||
|
import com.intellij.ide.highlighter.JavaClassFileType
|
||||||
|
import com.intellij.openapi.vfs.StandardFileSystems
|
||||||
|
import com.intellij.openapi.vfs.VfsUtilCore
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.openapi.vfs.VirtualFileSystem
|
||||||
|
import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.intellij.util.io.URLUtil
|
||||||
|
import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
public interface AbstractDeclarationFromLibraryModuleProvider {
|
||||||
|
public val scope: GlobalSearchScope
|
||||||
|
public val jarFileSystem: CoreJarFileSystem
|
||||||
|
|
||||||
|
public fun virtualFilesFromModule(
|
||||||
|
libraryModule: KtLibraryModule,
|
||||||
|
fqName: FqName,
|
||||||
|
isPackageName: Boolean,
|
||||||
|
): Collection<VirtualFile> {
|
||||||
|
val fqNameString = fqName.asString()
|
||||||
|
val fs = StandardFileSystems.local()
|
||||||
|
return libraryModule.getBinaryRoots().flatMap r@{ rootPath ->
|
||||||
|
val root = findRoot(rootPath, fs) ?: return@r emptySet()
|
||||||
|
val files = mutableSetOf<VirtualFile>()
|
||||||
|
VfsUtilCore.iterateChildrenRecursively(
|
||||||
|
root,
|
||||||
|
/*filter=*/filter@{
|
||||||
|
// Return `false` will skip the children.
|
||||||
|
if (it == root) return@filter true
|
||||||
|
// If it is a directory, then check if its path starts with fq name of interest
|
||||||
|
val relativeFqName = relativeFqName(root, it)
|
||||||
|
if (it.isDirectory && fqNameString.startsWith(relativeFqName)) {
|
||||||
|
return@filter true
|
||||||
|
}
|
||||||
|
// Otherwise, i.e., if it is a file, we are already in that matched directory (or directory in the middle).
|
||||||
|
// But, for files at the top-level, double-check if its parent (dir) and fq name of interest match.
|
||||||
|
if (isPackageName)
|
||||||
|
relativeFqName(root, it.parent).endsWith(fqNameString)
|
||||||
|
else // exact class fq name
|
||||||
|
relativeFqName == fqNameString
|
||||||
|
},
|
||||||
|
/*iterator=*/{
|
||||||
|
// We reach here after filtering above.
|
||||||
|
// Directories in the middle, e.g., com/android, can reach too.
|
||||||
|
if (!it.isDirectory &&
|
||||||
|
isCompiledFile(it) &&
|
||||||
|
it in scope
|
||||||
|
) {
|
||||||
|
files.add(it)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findRoot(
|
||||||
|
rootPath: Path,
|
||||||
|
fs: VirtualFileSystem,
|
||||||
|
): VirtualFile? {
|
||||||
|
return if (rootPath.toFile().isDirectory) {
|
||||||
|
fs.findFileByPath(rootPath.toAbsolutePath().toString())
|
||||||
|
} else {
|
||||||
|
jarFileSystem.refreshAndFindFileByPath(rootPath.toAbsolutePath().toString() + URLUtil.JAR_SEPARATOR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun relativeFqName(
|
||||||
|
root: VirtualFile,
|
||||||
|
virtualFile: VirtualFile,
|
||||||
|
): String {
|
||||||
|
return if (root.isDirectory) {
|
||||||
|
val fragments = buildList {
|
||||||
|
var cur = virtualFile
|
||||||
|
while (cur != root) {
|
||||||
|
add(cur.nameWithoutExtension)
|
||||||
|
cur = cur.parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fragments.reversed().joinToString(".")
|
||||||
|
} else {
|
||||||
|
virtualFile.path.split(URLUtil.JAR_SEPARATOR).lastOrNull()?.replace("/", ".")
|
||||||
|
?: URLUtil.JAR_SEPARATOR // random string that will bother membership test.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isCompiledFile(
|
||||||
|
virtualFile: VirtualFile,
|
||||||
|
): Boolean {
|
||||||
|
return virtualFile.extension?.endsWith(JavaClassFileType.INSTANCE.defaultExtension) == true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ dependencies {
|
|||||||
api(project(":analysis:analysis-api-fir"))
|
api(project(":analysis:analysis-api-fir"))
|
||||||
api(project(":analysis:low-level-api-fir"))
|
api(project(":analysis:low-level-api-fir"))
|
||||||
api(project(":analysis:symbol-light-classes"))
|
api(project(":analysis:symbol-light-classes"))
|
||||||
|
api(project(":analysis:decompiled:light-classes-for-decompiled"))
|
||||||
|
|
||||||
testApi(projectTests(":analysis:analysis-api-impl-base"))
|
testApi(projectTests(":analysis:analysis-api-impl-base"))
|
||||||
testApi(projectTests(":analysis:analysis-api-fir"))
|
testApi(projectTests(":analysis:analysis-api-fir"))
|
||||||
|
|||||||
+7
-8
@@ -17,20 +17,15 @@ import org.jetbrains.kotlin.analysis.api.InvalidWayOfUsingAnalysisSession
|
|||||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSessionProvider
|
import org.jetbrains.kotlin.analysis.api.KtAnalysisSessionProvider
|
||||||
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSessionProvider
|
import org.jetbrains.kotlin.analysis.api.fir.KtFirAnalysisSessionProvider
|
||||||
import org.jetbrains.kotlin.analysis.api.impl.base.references.HLApiReferenceProviderService
|
import org.jetbrains.kotlin.analysis.api.impl.base.references.HLApiReferenceProviderService
|
||||||
|
import org.jetbrains.kotlin.analysis.decompiled.light.classes.ClsJavaStubByVirtualFileCache
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.FirSealedClassInheritorsProcessorFactory
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.PackagePartProviderFactory
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.services.PackagePartProviderFactory
|
||||||
import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProvider
|
import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProvider
|
||||||
import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProviderImpl
|
import org.jetbrains.kotlin.analysis.project.structure.KtModuleScopeProviderImpl
|
||||||
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
|
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
|
||||||
import org.jetbrains.kotlin.analysis.project.structure.impl.ProjectStructureProviderByCompilerConfiguration
|
import org.jetbrains.kotlin.analysis.project.structure.impl.ProjectStructureProviderByCompilerConfiguration
|
||||||
import org.jetbrains.kotlin.analysis.providers.KotlinAnnotationsResolverFactory
|
import org.jetbrains.kotlin.analysis.providers.*
|
||||||
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProviderFactory
|
import org.jetbrains.kotlin.analysis.providers.impl.*
|
||||||
import org.jetbrains.kotlin.analysis.providers.KotlinModificationTrackerFactory
|
|
||||||
import org.jetbrains.kotlin.analysis.providers.KotlinPackageProviderFactory
|
|
||||||
import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticAnnotationsResolverFactory
|
|
||||||
import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticDeclarationProviderFactory
|
|
||||||
import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticModificationTrackerFactory
|
|
||||||
import org.jetbrains.kotlin.analysis.providers.impl.KotlinStaticPackageProviderFactory
|
|
||||||
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
|
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
|
||||||
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.javaSourceRoots
|
import org.jetbrains.kotlin.cli.jvm.config.javaSourceRoots
|
||||||
@@ -76,6 +71,7 @@ public fun configureApplicationEnvironment(app: MockApplication) {
|
|||||||
* * [KtAnalysisSessionProvider]
|
* * [KtAnalysisSessionProvider]
|
||||||
* * [KotlinAsJavaFirSupport]
|
* * [KotlinAsJavaFirSupport]
|
||||||
* * [SymbolLightClassFacadeCache] for FIR light class support
|
* * [SymbolLightClassFacadeCache] for FIR light class support
|
||||||
|
* * [ClsJavaStubByVirtualFileCache]
|
||||||
* * [KotlinModificationTrackerFactory]
|
* * [KotlinModificationTrackerFactory]
|
||||||
* * [KotlinAnnotationsResolverFactory]
|
* * [KotlinAnnotationsResolverFactory]
|
||||||
* * [LLFirResolveStateService]
|
* * [LLFirResolveStateService]
|
||||||
@@ -151,6 +147,9 @@ internal fun configureProjectEnvironment(
|
|||||||
project.registerService(
|
project.registerService(
|
||||||
SymbolLightClassFacadeCache::class.java
|
SymbolLightClassFacadeCache::class.java
|
||||||
)
|
)
|
||||||
|
project.registerService(
|
||||||
|
ClsJavaStubByVirtualFileCache::class.java
|
||||||
|
)
|
||||||
|
|
||||||
project.picoContainer.registerComponentInstance(
|
project.picoContainer.registerComponentInstance(
|
||||||
KotlinModificationTrackerFactory::class.qualifiedName,
|
KotlinModificationTrackerFactory::class.qualifiedName,
|
||||||
|
|||||||
+1
-1
@@ -54,7 +54,7 @@ internal class KtSourceModuleByCompilerConfiguration(
|
|||||||
ktFiles: List<KtFile>,
|
ktFiles: List<KtFile>,
|
||||||
jarFileSystem: CoreJarFileSystem,
|
jarFileSystem: CoreJarFileSystem,
|
||||||
) : BaseKtModuleByCompilerConfiguration(compilerConfig, project), KtSourceModule {
|
) : BaseKtModuleByCompilerConfiguration(compilerConfig, project), KtSourceModule {
|
||||||
override val directRegularDependencies: List<KtModule> by lazy {
|
override val directRegularDependencies: List<KtLibraryModule> by lazy {
|
||||||
val libraryRoots = compilerConfig.jvmModularRoots + compilerConfig.jvmClasspathRoots
|
val libraryRoots = compilerConfig.jvmModularRoots + compilerConfig.jvmClasspathRoots
|
||||||
val libraryRootsByType = libraryRoots.groupBy { it.isDirectory }
|
val libraryRootsByType = libraryRoots.groupBy { it.isDirectory }
|
||||||
buildList {
|
buildList {
|
||||||
|
|||||||
+9
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.project.structure.impl
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem
|
import com.intellij.openapi.vfs.impl.jar.CoreJarFileSystem
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.analysis.project.structure.KtLibraryModule
|
||||||
import org.jetbrains.kotlin.analysis.project.structure.KtModule
|
import org.jetbrains.kotlin.analysis.project.structure.KtModule
|
||||||
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
|
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
@@ -21,6 +22,10 @@ internal class ProjectStructureProviderByCompilerConfiguration(
|
|||||||
) : ProjectStructureProvider() {
|
) : ProjectStructureProvider() {
|
||||||
private val sourceModule = KtSourceModuleByCompilerConfiguration(compilerConfig, project, ktFiles, jarFileSystem)
|
private val sourceModule = KtSourceModuleByCompilerConfiguration(compilerConfig, project, ktFiles, jarFileSystem)
|
||||||
|
|
||||||
|
private val libraryModules: Collection<KtLibraryModule> by lazy {
|
||||||
|
sourceModule.directRegularDependencies
|
||||||
|
}
|
||||||
|
|
||||||
override fun getKtModuleForKtElement(element: PsiElement): KtModule {
|
override fun getKtModuleForKtElement(element: PsiElement): KtModule {
|
||||||
val containingFile = element.containingFile.virtualFile
|
val containingFile = element.containingFile.virtualFile
|
||||||
return if (containingFile in sourceModule.contentScope) {
|
return if (containingFile in sourceModule.contentScope) {
|
||||||
@@ -30,4 +35,8 @@ internal class ProjectStructureProviderByCompilerConfiguration(
|
|||||||
?: error("Can't find module for ${containingFile.path}")
|
?: error("Can't find module for ${containingFile.path}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getKtLibraryModules(): Collection<KtLibraryModule> {
|
||||||
|
return libraryModules
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -13,6 +13,12 @@ public abstract class ProjectStructureProvider {
|
|||||||
* For a given [PsiElement] get a [KtModule] to which [PsiElement] belongs.
|
* For a given [PsiElement] get a [KtModule] to which [PsiElement] belongs.
|
||||||
*/
|
*/
|
||||||
public abstract fun getKtModuleForKtElement(element: PsiElement): KtModule
|
public abstract fun getKtModuleForKtElement(element: PsiElement): KtModule
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all [KtLibraryModule]s in this project.
|
||||||
|
*/
|
||||||
|
// TODO: We rather need a session or facade that maintains this information.
|
||||||
|
public abstract fun getKtLibraryModules(): Collection<KtLibraryModule>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,4 +43,4 @@ public inline fun <reified M : KtModule> PsiElement.getKtModuleOfType(project: P
|
|||||||
* @param project [Project] which contains current [PsiElement]. `PsiElement.project` may be a heavy operation as it includes PSI tree traversal. So, when a [Project] is already available, it is better to pass it explicitly
|
* @param project [Project] which contains current [PsiElement]. `PsiElement.project` may be a heavy operation as it includes PSI tree traversal. So, when a [Project] is already available, it is better to pass it explicitly
|
||||||
*/
|
*/
|
||||||
public inline fun <reified M : KtModule> PsiElement.getKtModuleOfTypeSafe(project: Project = this.project): M? =
|
public inline fun <reified M : KtModule> PsiElement.getKtModuleOfTypeSafe(project: Project = this.project): M? =
|
||||||
getKtModule(project) as M?
|
getKtModule(project) as M?
|
||||||
|
|||||||
Reference in New Issue
Block a user