Split LightClassGenerationSupport into separate entities

LightClassGenerationSupport:
    Includes utilities related to generating light classes
    Primary client of this service are KtLight* implementation classes
KotlinAsJavaSupport:
    Provides APIs to transform kotlin code into Psi* entities
    Primary client of this service is JavaElementFinder
This commit is contained in:
Pavel V. Talanov
2018-03-08 19:05:38 +01:00
parent 62d409a9ba
commit f421181e16
21 changed files with 268 additions and 228 deletions
@@ -20,7 +20,9 @@ import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.asJava.builder.*
import org.jetbrains.kotlin.asJava.builder.LightClassBuilderResult
import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForScript
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
@@ -30,31 +32,24 @@ import org.jetbrains.kotlin.resolve.BindingContext
typealias LightClassBuilder = (LightClassConstructionContext) -> LightClassBuilderResult
abstract class LightClassGenerationSupport {
abstract fun createDataHolderForClass(classOrObject: KtClassOrObject, builder: LightClassBuilder): LightClassDataHolder.ForClass
abstract fun createDataHolderForFacade(files: Collection<KtFile>, builder: LightClassBuilder): LightClassDataHolder.ForFacade
abstract fun createDataHolderForScript(script: KtScript, builder: LightClassBuilder): LightClassDataHolder.ForScript
abstract fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtClassOrObject>
/*
* Finds files whose package declaration is exactly {@code fqName}. For example, if a file declares
* package a.b.c
* it will not be returned for fqName "a.b"
*
* If the resulting collection is empty, it means that this package has not other declarations than sub-packages
*/
abstract fun findFilesForPackage(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtFile>
abstract class KotlinAsJavaSupport {
// Returns only immediately declared classes/objects, package classes are not included (they have no declarations)
abstract fun findClassOrObjectDeclarationsInPackage(
packageFqName: FqName,
searchScope: GlobalSearchScope
packageFqName: FqName,
searchScope: GlobalSearchScope
): Collection<KtClassOrObject>
/*
* Finds files whose package declaration is exactly {@code fqName}. For example, if a file declares
* package a.b.c
* it will not be returned for fqName "a.b"
*
* If the resulting collection is empty, it means that this package has not other declarations than sub-packages
*/
abstract fun findFilesForPackage(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtFile>
abstract fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtClassOrObject>
abstract fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean
abstract fun getSubPackages(fqn: FqName, scope: GlobalSearchScope): Collection<FqName>
@@ -63,12 +58,6 @@ abstract class LightClassGenerationSupport {
abstract fun getLightClassForScript(script: KtScript): KtLightClassForScript?
abstract fun resolveToDescriptor(declaration: KtDeclaration): DeclarationDescriptor?
abstract fun analyze(element: KtElement): BindingContext
abstract fun analyzeWithContent(element: KtClassOrObject): BindingContext
abstract fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
abstract fun getScriptClasses(scriptFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass>
@@ -82,7 +71,29 @@ abstract class LightClassGenerationSupport {
abstract fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<KtFile>
companion object {
@JvmStatic fun getInstance(project: Project): LightClassGenerationSupport {
@JvmStatic
fun getInstance(project: Project): KotlinAsJavaSupport {
return ServiceManager.getService(project, KotlinAsJavaSupport::class.java)
}
}
}
abstract class LightClassGenerationSupport {
abstract fun createDataHolderForClass(classOrObject: KtClassOrObject, builder: LightClassBuilder): LightClassDataHolder.ForClass
abstract fun createDataHolderForFacade(files: Collection<KtFile>, builder: LightClassBuilder): LightClassDataHolder.ForFacade
abstract fun createDataHolderForScript(script: KtScript, builder: LightClassBuilder): LightClassDataHolder.ForScript
abstract fun resolveToDescriptor(declaration: KtDeclaration): DeclarationDescriptor?
abstract fun analyze(element: KtElement): BindingContext
abstract fun analyzeWithContent(element: KtClassOrObject): BindingContext
companion object {
@JvmStatic
fun getInstance(project: Project): LightClassGenerationSupport {
return ServiceManager.getService(project, LightClassGenerationSupport::class.java)
}
}
@@ -23,6 +23,7 @@ import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.classes.getOutermostClassOrObject
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
@@ -114,7 +115,7 @@ sealed class LightClassDataProviderForFileFacade constructor(
facadeFqName: FqName,
private val searchScope: GlobalSearchScope
) : LightClassDataProviderForFileFacade(project, facadeFqName) {
override fun findFiles() = LightClassGenerationSupport.getInstance(project).findFilesForFacade(facadeFqName, searchScope)
override fun findFiles() = KotlinAsJavaSupport.getInstance(project).findFilesForFacade(facadeFqName, searchScope)
}
// create delegate by single file
@@ -29,11 +29,14 @@ import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport;
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.FqNamesUtilKt;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.KtClass;
import org.jetbrains.kotlin.psi.KtClassOrObject;
import org.jetbrains.kotlin.psi.KtEnumEntry;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.resolve.jvm.KotlinFinderMarker;
import java.util.Collection;
@@ -58,15 +61,15 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM
private final Project project;
private final PsiManager psiManager;
private final LightClassGenerationSupport lightClassGenerationSupport;
private final KotlinAsJavaSupport kotlinAsJavaSupport;
public JavaElementFinder(
@NotNull Project project,
@NotNull LightClassGenerationSupport lightClassGenerationSupport
@NotNull KotlinAsJavaSupport kotlinAsJavaSupport
) {
this.project = project;
this.psiManager = PsiManager.getInstance(project);
this.lightClassGenerationSupport = lightClassGenerationSupport;
this.kotlinAsJavaSupport = kotlinAsJavaSupport;
}
@Override
@@ -87,8 +90,8 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM
FqName qualifiedName = new FqName(qualifiedNameString);
findClassesAndObjects(qualifiedName, scope, answer);
answer.addAll(lightClassGenerationSupport.getFacadeClasses(qualifiedName, scope));
answer.addAll(lightClassGenerationSupport.getKotlinInternalClasses(qualifiedName, scope));
answer.addAll(kotlinAsJavaSupport.getFacadeClasses(qualifiedName, scope));
answer.addAll(kotlinAsJavaSupport.getKotlinInternalClasses(qualifiedName, scope));
return sortByClasspath(answer, scope).toArray(new PsiClass[answer.size()]);
}
@@ -99,7 +102,7 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM
findInterfaceDefaultImpls(qualifiedName, scope, answer);
Collection<KtClassOrObject> classOrObjectDeclarations =
lightClassGenerationSupport.findClassOrObjectDeclarations(qualifiedName, scope);
kotlinAsJavaSupport.findClassOrObjectDeclarations(qualifiedName, scope);
for (KtClassOrObject declaration : classOrObjectDeclarations) {
if (!(declaration instanceof KtEnumEntry)) {
@@ -116,7 +119,7 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM
if (!qualifiedName.shortName().asString().equals(JvmAbi.DEFAULT_IMPLS_CLASS_NAME)) return;
for (KtClassOrObject classOrObject : lightClassGenerationSupport.findClassOrObjectDeclarations(qualifiedName.parent(), scope)) {
for (KtClassOrObject classOrObject : kotlinAsJavaSupport.findClassOrObjectDeclarations(qualifiedName.parent(), scope)) {
//NOTE: can't filter out more interfaces right away because decompiled declarations do not have member bodies
if (classOrObject instanceof KtClass && ((KtClass) classOrObject).isInterface()) {
PsiClass interfaceClass = toLightClass(classOrObject);
@@ -135,10 +138,10 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM
public Set<String> getClassNames(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) {
FqName packageFQN = new FqName(psiPackage.getQualifiedName());
Collection<KtClassOrObject> declarations = lightClassGenerationSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope);
Collection<KtClassOrObject> declarations = kotlinAsJavaSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope);
Set<String> answer = Sets.newHashSet();
answer.addAll(lightClassGenerationSupport.getFacadeNames(packageFQN, scope));
answer.addAll(kotlinAsJavaSupport.getFacadeNames(packageFQN, scope));
for (KtClassOrObject declaration : declarations) {
String name = declaration.getName();
@@ -160,7 +163,7 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM
// allScope() because the contract says that the whole project
GlobalSearchScope allScope = GlobalSearchScope.allScope(project);
if (lightClassGenerationSupport.packageExists(fqName, allScope)) {
if (kotlinAsJavaSupport.packageExists(fqName, allScope)) {
return new KtLightPackage(psiManager, fqName, allScope);
}
@@ -172,7 +175,7 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM
public PsiPackage[] getSubPackages(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) {
FqName packageFQN = new FqName(psiPackage.getQualifiedName());
Collection<FqName> subpackages = lightClassGenerationSupport.getSubPackages(packageFQN, scope);
Collection<FqName> subpackages = kotlinAsJavaSupport.getSubPackages(packageFQN, scope);
Collection<PsiPackage> answer = Collections2.transform(subpackages, input -> new KtLightPackage(psiManager, input, scope));
@@ -185,9 +188,9 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM
List<PsiClass> answer = new SmartList<>();
FqName packageFQN = new FqName(psiPackage.getQualifiedName());
answer.addAll(lightClassGenerationSupport.getFacadeClassesInPackage(packageFQN, scope));
answer.addAll(kotlinAsJavaSupport.getFacadeClassesInPackage(packageFQN, scope));
Collection<KtClassOrObject> declarations = lightClassGenerationSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope);
Collection<KtClassOrObject> declarations = kotlinAsJavaSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope);
for (KtClassOrObject declaration : declarations) {
PsiClass aClass = toLightClass(declaration);
if (aClass != null) {
@@ -203,7 +206,7 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM
public PsiFile[] getPackageFiles(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) {
FqName packageFQN = new FqName(psiPackage.getQualifiedName());
// TODO: this does not take into account JvmPackageName annotation
Collection<KtFile> result = lightClassGenerationSupport.findFilesForPackage(packageFQN, scope);
Collection<KtFile> result = kotlinAsJavaSupport.findFilesForPackage(packageFQN, scope);
return result.toArray(new PsiFile[result.size()]);
}
@@ -21,7 +21,7 @@ import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.file.PsiPackageImpl;
import com.intellij.psi.search.GlobalSearchScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport;
import org.jetbrains.kotlin.asJava.KotlinAsJavaSupport;
import org.jetbrains.kotlin.name.FqName;
public class KtLightPackage extends PsiPackageImpl {
@@ -43,6 +43,6 @@ public class KtLightPackage extends PsiPackageImpl {
@Override
public boolean isValid() {
return LightClassGenerationSupport.getInstance(getProject()).packageExists(fqName, scope);
return KotlinAsJavaSupport.getInstance(getProject()).packageExists(fqName, scope);
}
}
@@ -36,7 +36,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration
fun KtClassOrObject.toLightClass(): KtLightClass? = LightClassGenerationSupport.getInstance(project).getLightClass(this)
fun KtClassOrObject.toLightClass(): KtLightClass? = KotlinAsJavaSupport.getInstance(project).getLightClass(this)
fun KtClassOrObject.toLightClassWithBuiltinMapping(): PsiClass? {
toLightClass()?.let { return it }
@@ -48,12 +48,12 @@ fun KtClassOrObject.toLightClassWithBuiltinMapping(): PsiClass? {
}
fun KtFile.findFacadeClass(): KtLightClass? {
return LightClassGenerationSupport.getInstance(project)
return KotlinAsJavaSupport.getInstance(project)
.getFacadeClassesInPackage(packageFqName, this.useScope as? GlobalSearchScope ?: GlobalSearchScope.projectScope(project))
.firstOrNull { it is KtLightClassForFacade && this in it.files } as? KtLightClass
}
fun KtScript.toLightClass(): KtLightClassForScript? = LightClassGenerationSupport.getInstance(project).getLightClassForScript(this)
fun KtScript.toLightClass(): KtLightClassForScript? = KotlinAsJavaSupport.getInstance(project).getLightClassForScript(this)
fun KtElement.toLightElements(): List<PsiNamedElement> =
when (this) {