Light classes in IDE: Make light class delegate construction a two step process

Step 0: Light class object is created, no delegates are computed
Step 1: constructs dummy delegate which can not be relied upon to know signature of any member
		It can be used to construct light field and light method objects
		(which can correctly respond to some queries) before constructing real delegate
Step 2:
		Construct real delegate if dummy delegate is not enough to respond to a query

This speeds up multiple scenarios where getMethods() and getFields() are called on numerous classes

Dummy delegate's faster consruction is achieved by using specially setup dumb analysis instead of ide analysis

Introduce LazyLightClassDataHolder: which manages creation of Step 1 and Step 2 delegates
Introduce MemberIndex: keeping track of member creation order, helps matching light class delegates created in different contexts
KtLightMethod and Field: make use of dummy delegate
KtLightMethod no longer extends LightMethod since it requires eager delegate construction
KtLightMethod now implements PsiAnnotationMethod for convenience (ClsMethodImpl implements PsiAnnotationMethod)
This commit is contained in:
Pavel V. Talanov
2017-02-16 16:26:11 +03:00
parent ed9e94c632
commit daef8a0eed
15 changed files with 683 additions and 200 deletions
@@ -17,15 +17,23 @@
package org.jetbrains.kotlin.asJava.builder
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiClassType
import com.intellij.psi.impl.DebugUtil
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import com.intellij.psi.stubs.StubElement
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.LightClassUtil.findClass
import org.jetbrains.kotlin.asJava.builder.InvalidLightClassDataHolder.javaFileStub
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.getOutermostClassOrObject
import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightFieldImpl
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.elements.KtLightMethodImpl
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.debugText.getDebugText
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
interface LightClassDataHolder {
@@ -33,15 +41,33 @@ interface LightClassDataHolder {
val extraDiagnostics: Diagnostics
fun findData(findDelegate: (PsiJavaFileStub) -> PsiClass): LightClassData
fun findData(classOrObject: KtClassOrObject): LightClassData = findData { it.findDelegate(classOrObject) }
fun findData(classFqName: FqName): LightClassData = findData { it.findDelegate(classFqName) }
fun findDataForDefaultImpls(classOrObject: KtClassOrObject) = findData {
it.findDelegate(classOrObject).findInnerClassByName(JvmAbi.DEFAULT_IMPLS_CLASS_NAME, false)
?: throw IllegalStateException("Couldn't get delegate for $this\n in ${DebugUtil.stubTreeToString(it)}")
}
fun findDataForClassOrObject(classOrObject: KtClassOrObject): LightClassData = findData { it.findDelegate(classOrObject) }
fun findDataForFacade(classFqName: FqName): LightClassData = findData { it.findDelegate(classFqName) }
}
interface LightClassData {
val clsDelegate: PsiClass
val supertypes: Array<PsiClassType> get() { return clsDelegate.superTypes }
fun getOwnFields(containingClass: KtLightClass): List<KtLightField>
fun getOwnMethods(containingClass: KtLightClass): List<KtLightMethod>
}
class LightClassDataImpl(override val clsDelegate: PsiClass) : LightClassData {
override fun getOwnFields(containingClass: KtLightClass): List<KtLightField> {
return clsDelegate.fields.map { KtLightFieldImpl.fromClsField(it, containingClass) }
}
override fun getOwnMethods(containingClass: KtLightClass): List<KtLightMethod> {
return clsDelegate.methods.map { KtLightMethodImpl.fromClsMethod(it, containingClass) }
}
}
object InvalidLightClassDataHolder : LightClassDataHolder {
@@ -77,7 +103,7 @@ fun PsiJavaFileStub.findDelegate(classOrObject: KtClassOrObject): PsiClass {
}
val stubFileText = DebugUtil.stubTreeToString(this)
throw IllegalStateException("Couldn't get delegate for $this\nin $ktFileText\nstub: \n$stubFileText")
throw IllegalStateException("Couldn't get delegate for ${classOrObject.getDebugText()}\nin $ktFileText\nstub: \n$stubFileText")
}
fun PsiJavaFileStub.findDelegate(classFqName: FqName): PsiClass {
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.asJava.builder
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.UserDataHolder
import com.intellij.psi.PsiMember
import com.intellij.psi.StubBasedPsiElement
data class MemberIndex(private val index: Int) {
companion object {
@JvmField
val KEY = Key.create<MemberIndex>("MEMBER_INDEX")
}
}
val PsiMember.memberIndex: MemberIndex?
get() = ((this as? StubBasedPsiElement<*>)?.stub as? UserDataHolder)?.getUserData(MemberIndex.KEY)
@@ -26,8 +26,8 @@ import com.intellij.psi.stubs.StubElement;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.fileClasses.OldPackageFacadeClassUtils;
import org.jetbrains.kotlin.codegen.AbstractClassBuilder;
import org.jetbrains.kotlin.fileClasses.OldPackageFacadeClassUtils;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
@@ -53,6 +53,7 @@ public class StubClassBuilder extends AbstractClassBuilder {
private StubBuildingVisitor v;
private final Stack<StubElement> parentStack;
private boolean isPackageClass = false;
private int memberIndex = 0;
public StubClassBuilder(@NotNull Stack<StubElement> parentStack) {
this.parentStack = parentStack;
@@ -196,6 +197,7 @@ public class StubClassBuilder extends AbstractClassBuilder {
}
last.putUserData(ClsWrapperStubPsiFactory.ORIGIN, LightElementOriginKt.toLightMemberOrigin(origin));
last.putUserData(MemberIndex.KEY, new MemberIndex(memberIndex++));
}
@Override
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.asJava.classes
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.asJava.builder.LightClassData
abstract class KtLazyLightClass(manager: PsiManager) : KtLightClassBase(manager) {
abstract val lightClassData: LightClassData
override fun getOwnFields() = lightClassData.getOwnFields(this)
override fun getOwnMethods() = lightClassData.getOwnMethods(this)
override fun getSuperTypes() = lightClassData.supertypes
}
@@ -51,7 +51,7 @@ class KtLightClassForFacade private constructor(
private val facadeClassFqName: FqName,
private val lightClassDataCache: CachedValue<LightClassDataHolder>,
files: Collection<KtFile>
) : KtLightClassBase(manager) {
) : KtLazyLightClass(manager) {
private data class StubCacheKey(val fqName: FqName, val searchScope: GlobalSearchScope)
class FacadeStubCache(private val project: Project) {
@@ -194,8 +194,12 @@ class KtLightClassForFacade private constructor(
override fun copy() = KtLightClassForFacade(manager, facadeClassFqName, lightClassDataCache, files)
override val lightClassData by lazy(LazyThreadSafetyMode.PUBLICATION) {
lightClassDataCache.value.findDataForFacade(facadeClassFqName)
}
override val clsDelegate: PsiClass
get() = lightClassDataCache.value.findData(facadeClassFqName).clsDelegate
get() = lightClassData.clsDelegate
override fun getNavigationElement() = files.iterator().next()
@@ -17,15 +17,12 @@
package org.jetbrains.kotlin.asJava.classes
import com.intellij.psi.*
import com.intellij.psi.impl.DebugUtil
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.asJava.builder.LightClassData
import org.jetbrains.kotlin.asJava.builder.findDelegate
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.KtClassOrObject
class KtLightClassForInterfaceDefaultImpls(
classOrObject: KtClassOrObject)
class KtLightClassForInterfaceDefaultImpls(classOrObject: KtClassOrObject)
: KtLightClassForSourceDeclaration(classOrObject) {
override fun getQualifiedName(): String? = containingClass?.qualifiedName?.let { it + ".${JvmAbi.DEFAULT_IMPLS_CLASS_NAME}" }
@@ -37,10 +34,7 @@ class KtLightClassForInterfaceDefaultImpls(
}
override fun findLightClassData(): LightClassData {
return getLightClassDataHolder().findData {
it.findDelegate(classOrObject).findInnerClassByName(JvmAbi.DEFAULT_IMPLS_CLASS_NAME, false)
?: throw IllegalStateException("Couldn't get delegate for $this\n in ${DebugUtil.stubTreeToString(it)}")
}
return getLightClassDataHolder().findDataForDefaultImpls(classOrObject)
}
override fun getTypeParameterList(): PsiTypeParameterList? = null
@@ -59,7 +59,7 @@ import java.util.*
import javax.swing.Icon
abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtClassOrObject)
: KtLightClassBase(classOrObject.manager), StubBasedPsiElement<KotlinClassOrObjectStub<out KtClassOrObject>> {
: KtLazyLightClass(classOrObject.manager), StubBasedPsiElement<KotlinClassOrObjectStub<out KtClassOrObject>> {
private val lightIdentifier = KtLightIdentifier(this, classOrObject)
private val _extendsList by lazy(LazyThreadSafetyMode.PUBLICATION) {
@@ -80,9 +80,9 @@ abstract class KtLightClassForSourceDeclaration(protected val classOrObject: KtC
override val clsDelegate: PsiClass get() = lightClassData.clsDelegate
private val lightClassData: LightClassData by lazy(LazyThreadSafetyMode.PUBLICATION) { findLightClassData() }
override val lightClassData: LightClassData by lazy(LazyThreadSafetyMode.PUBLICATION) { findLightClassData() }
open protected fun findLightClassData() = getLightClassDataHolder().findData(classOrObject)
open protected fun findLightClassData() = getLightClassDataHolder().findDataForClassOrObject(classOrObject)
private fun getJavaFileStub(): PsiJavaFileStub = getLightClassDataHolder().javaFileStub
@@ -36,13 +36,16 @@ import org.jetbrains.kotlin.psi.KtNamedDeclaration
import java.lang.UnsupportedOperationException
// Copied from com.intellij.psi.impl.light.LightField
sealed class KtLightFieldImpl(
sealed class KtLightFieldImpl<T: PsiField>(
override val lightMemberOrigin: LightMemberOrigin?,
override val clsDelegate: PsiField,
private val containingClass: KtLightClass
) : LightElement(clsDelegate.manager, KotlinLanguage.INSTANCE), KtLightField {
computeRealDelegate: () -> T,
private val containingClass: KtLightClass,
private val dummyDelegate: PsiField?
) : LightElement(containingClass.manager, KotlinLanguage.INSTANCE), KtLightField {
private val lightIdentifier by lazy(LazyThreadSafetyMode.PUBLICATION) { KtLightIdentifier(this, kotlinOrigin as? KtNamedDeclaration) }
override val clsDelegate: T by lazy(LazyThreadSafetyMode.PUBLICATION, computeRealDelegate)
@Throws(IncorrectOperationException::class)
override fun setInitializer(initializer: PsiExpression?) = throw IncorrectOperationException("Not supported")
@@ -50,7 +53,7 @@ sealed class KtLightFieldImpl(
override fun getPresentation(): ItemPresentation? = (kotlinOrigin ?: this).let { ItemPresentationProviders.getItemPresentation(it) }
override fun getName() = clsDelegate.name
override fun getName() = dummyDelegate?.name ?: clsDelegate.name
override fun getNameIdentifier() = lightIdentifier
@@ -134,41 +137,58 @@ sealed class KtLightFieldImpl(
class KtLightEnumConstant(
origin: LightMemberOrigin?,
override val clsDelegate: PsiEnumConstant,
computeDelegate: () -> PsiEnumConstant,
containingClass: KtLightClass,
private val initializingClass: PsiEnumConstantInitializer?
) : KtLightFieldImpl(origin, clsDelegate, containingClass), PsiEnumConstant {
dummyDelegate: PsiField?
) : KtLightFieldImpl<PsiEnumConstant>(origin, computeDelegate , containingClass, dummyDelegate), PsiEnumConstant {
private val initializingClass by lazy(LazyThreadSafetyMode.PUBLICATION) {
val kotlinEnumEntry = (lightMemberOrigin as? LightMemberOriginForDeclaration)?.originalElement as? KtEnumEntry
if (kotlinEnumEntry != null && kotlinEnumEntry.declarations.isNotEmpty()) {
KtLightClassForEnumEntry(kotlinEnumEntry, clsDelegate)
}
else null
}
// NOTE: we don't use "delegation by" because the compiler would generate method calls to ALL of PsiEnumConstant members,
// but we need only members whose implementations are not present in KotlinLightField
override fun getArgumentList() = clsDelegate.argumentList
override fun getInitializingClass(): PsiEnumConstantInitializer? = initializingClass
override fun getOrCreateInitializingClass(): PsiEnumConstantInitializer =
initializingClass ?: throw UnsupportedOperationException("Can't create enum constant body: ${clsDelegate.name}")
override fun getOrCreateInitializingClass(): PsiEnumConstantInitializer {
return initializingClass ?: throw UnsupportedOperationException("Can't create enum constant body: ${clsDelegate.name}")
}
override fun resolveConstructor() = clsDelegate.resolveConstructor()
override fun resolveMethod() = clsDelegate.resolveMethod()
override fun resolveMethodGenerics() = clsDelegate.resolveMethodGenerics()
}
class KtLightFieldForDeclaration(origin: LightMemberOrigin?, delegate: PsiField, containingClass: KtLightClass) :
KtLightFieldImpl(origin, delegate, containingClass)
class KtLightFieldForDeclaration(origin: LightMemberOrigin?, computeDelegate: () -> PsiField, containingClass: KtLightClass, dummyDelegate: PsiField?) :
KtLightFieldImpl<PsiField>(origin, computeDelegate, containingClass, dummyDelegate)
companion object Factory {
fun create(origin: LightMemberOrigin?, delegate: PsiField, containingClass: KtLightClass): KtLightField {
when (delegate) {
is PsiEnumConstant -> {
val kotlinEnumEntry = (origin as? LightMemberOriginForDeclaration)?.originalElement as? KtEnumEntry
val initializingClass = if (kotlinEnumEntry != null && kotlinEnumEntry.declarations.isNotEmpty()) {
KtLightClassForEnumEntry(kotlinEnumEntry, delegate)
}
else null
return KtLightEnumConstant(origin, delegate, containingClass, initializingClass)
return KtLightEnumConstant(origin, { delegate }, containingClass, null)
}
else -> return KtLightFieldForDeclaration(origin, delegate, containingClass)
else -> return KtLightFieldForDeclaration(origin, { delegate }, containingClass, null)
}
}
fun lazy(
dummyDelegate: PsiField,
origin: LightMemberOriginForDeclaration,
containingClass: KtLightClass,
computeRealDelegate: () -> PsiField
): KtLightField {
if (dummyDelegate is PsiEnumConstant) {
@Suppress("UNCHECKED_CAST")
return KtLightEnumConstant(origin, computeRealDelegate as () -> PsiEnumConstant, containingClass, dummyDelegate)
}
return KtLightFieldForDeclaration(origin, computeRealDelegate, containingClass, dummyDelegate)
}
@JvmStatic
fun fromClsField(field: PsiField, containingClass: KtLightClass): KtLightField {
val origin = ClsWrapperStubPsiFactory.getMemberOrigin(field)
@@ -21,8 +21,8 @@ import com.intellij.navigation.ItemPresentation
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.impl.compiled.ClsTypeElementImpl
import com.intellij.psi.impl.light.LightMethod
import com.intellij.psi.impl.light.LightModifierList
import com.intellij.psi.impl.light.LightElement
import com.intellij.psi.javadoc.PsiDocComment
import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.psi.util.*
import com.intellij.util.IncorrectOperationException
@@ -35,22 +35,26 @@ import org.jetbrains.kotlin.asJava.propertyNameByAccessor
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.jvm.KotlinJavaPsiFacade
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
interface KtLightMethod : PsiMethod, KtLightDeclaration<KtDeclaration, PsiMethod> {
interface KtLightMethod : PsiAnnotationMethod, KtLightDeclaration<KtDeclaration, PsiMethod> {
val lightMethodOrigin: LightMemberOrigin?
val isDelegated: Boolean
}
sealed class KtLightMethodImpl(
override val clsDelegate: PsiMethod,
class KtLightMethodImpl private constructor(
computeRealDelegate: () -> PsiMethod,
override val lightMethodOrigin: LightMemberOrigin?,
containingClass: KtLightClass
) : LightMethod(clsDelegate.manager, clsDelegate, containingClass), KtLightMethod {
private val containingClass: KtLightClass,
private val dummyDelegate: PsiMethod? = null
) : LightElement(containingClass.manager, containingClass.language), KtLightMethod {
override val kotlinOrigin: KtDeclaration? get() = lightMethodOrigin?.originalElement as? KtDeclaration
override val clsDelegate by lazy(LazyThreadSafetyMode.PUBLICATION, computeRealDelegate)
private val lightIdentifier by lazy(LazyThreadSafetyMode.PUBLICATION) { KtLightIdentifier(this, kotlinOrigin as? KtNamedDeclaration) }
private val returnTypeElem by lazy(LazyThreadSafetyMode.PUBLICATION) {
val delegateTypeElement = clsDelegate.returnTypeElement as? ClsTypeElementImpl
@@ -59,7 +63,7 @@ sealed class KtLightMethodImpl(
private val calculatingReturnType = ThreadLocal<Boolean>()
override fun getContainingClass(): KtLightClass = super.getContainingClass() as KtLightClass
override fun getContainingClass(): KtLightClass = containingClass
private val paramsList: CachedValue<PsiParameterList> by lazy(LazyThreadSafetyMode.PUBLICATION) {
val cacheManager = CachedValuesManager.getManager(clsDelegate.project)
@@ -95,7 +99,7 @@ sealed class KtLightMethodImpl(
override fun getNavigationElement(): PsiElement = kotlinOrigin?.navigationElement ?: super.getNavigationElement()
override fun getPresentation(): ItemPresentation? = kotlinOrigin?.presentation ?: super.getPresentation()
override fun getParent(): PsiElement? = containingClass
override fun getParent(): PsiElement = containingClass
override fun getText() = kotlinOrigin?.text ?: ""
override fun getTextRange() = kotlinOrigin?.textRange ?: TextRange.EMPTY_RANGE
@@ -198,21 +202,11 @@ sealed class KtLightMethodImpl(
containingClass == other.containingClass &&
clsDelegate == other.clsDelegate
override fun hashCode(): Int = ((name.hashCode() * 31 + (lightMethodOrigin?.hashCode() ?: 0)) * 31 + containingClass.hashCode()) * 31 + clsDelegate.hashCode()
override fun hashCode(): Int = ((getName().hashCode() * 31 + (lightMethodOrigin?.hashCode() ?: 0)) * 31 + containingClass.hashCode()) * 31 + clsDelegate.hashCode()
override fun toString(): String = "${this::class.java.simpleName}:$name"
private class KtLightMethodForDeclaration(
delegate: PsiMethod, origin: LightMemberOrigin?, containingClass: KtLightClass
) : KtLightMethodImpl(delegate, origin, containingClass)
class KtLightAnnotationMethod(
override val clsDelegate: PsiAnnotationMethod,
origin: LightMemberOrigin?,
containingClass: KtLightClass
) : KtLightMethodImpl(clsDelegate, origin, containingClass), PsiAnnotationMethod {
override fun getDefaultValue() = clsDelegate.defaultValue
}
override fun getDefaultValue() = (clsDelegate as? PsiAnnotationMethod)?.defaultValue
// override getReturnType() so return type resolves to type parameters of this method not delegate's
// which is relied upon by java type inference
@@ -229,27 +223,72 @@ sealed class KtLightMethodImpl(
}
companion object Factory {
private fun adjustMethodOrigin(origin: LightMemberOriginForDeclaration?): LightMemberOriginForDeclaration? {
val originalElement = origin?.originalElement
if (originalElement is KtPropertyAccessor) {
return origin.copy(originalElement.getStrictParentOfType<KtProperty>()!!, origin.originKind)
}
return origin
}
fun create(
delegate: PsiMethod, origin: LightMemberOrigin?, containingClass: KtLightClass
): KtLightMethodImpl {
return when (delegate) {
is PsiAnnotationMethod -> KtLightAnnotationMethod(delegate, origin, containingClass)
else -> KtLightMethodForDeclaration(delegate, origin, containingClass)
}
return KtLightMethodImpl({ delegate}, origin, containingClass)
}
@JvmStatic
fun fromClsMethod(method: PsiMethod, containingClass: KtLightClass): KtLightMethodImpl {
var origin = ClsWrapperStubPsiFactory.getMemberOrigin(method)
val originalElement = if (origin != null) origin.originalElement else null
if (originalElement is KtPropertyAccessor) {
val origin = ClsWrapperStubPsiFactory.getMemberOrigin(method)
return KtLightMethodImpl.create(method, adjustMethodOrigin(origin), containingClass)
}
origin = origin!!.copy(PsiTreeUtil.getParentOfType(originalElement, KtProperty::class.java)!!, origin.originKind)
}
return KtLightMethodImpl.create(method, origin, containingClass)
@JvmStatic
fun lazy(
dummyDelegate: PsiMethod?,
containingClass: KtLightClass,
origin: LightMemberOriginForDeclaration?,
computeRealDelegate: () -> PsiMethod
): KtLightMethodImpl {
return KtLightMethodImpl(computeRealDelegate, adjustMethodOrigin(origin), containingClass, dummyDelegate)
}
}
override fun getName() = dummyDelegate?.name ?: clsDelegate.name
override fun hasModifierProperty(name: String) = clsDelegate.hasModifierProperty(name)
override fun getThrowsList() = clsDelegate.throwsList
override fun hasTypeParameters() = clsDelegate.hasTypeParameters()
override fun isVarArgs() = clsDelegate.isVarArgs
override fun isConstructor() = clsDelegate.isConstructor
override fun getHierarchicalMethodSignature() = clsDelegate.hierarchicalMethodSignature
override fun getDocComment() = clsDelegate.docComment
override fun findSuperMethodSignaturesIncludingStatic(checkAccess: Boolean) = clsDelegate.findSuperMethodSignaturesIncludingStatic(checkAccess)
override fun getBody() = null
override fun isDeprecated() = clsDelegate.isDeprecated
override fun findDeepestSuperMethod() = clsDelegate.findDeepestSuperMethod()
override fun findDeepestSuperMethods() = clsDelegate.findDeepestSuperMethods()
override fun findSuperMethods() = clsDelegate.findSuperMethods()
override fun findSuperMethods(checkAccess: Boolean) = clsDelegate.findSuperMethods(checkAccess)
override fun findSuperMethods(parentClass: PsiClass?) = clsDelegate.findSuperMethods(parentClass)
override fun getContainingFile() = parent.containingFile
override fun isValid() = containingClass.isValid
}
fun KtLightMethod.isTraitFakeOverride(): Boolean {
@@ -28,34 +28,27 @@ import com.intellij.psi.impl.compiled.ClsFileImpl
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.builder.*
import org.jetbrains.kotlin.asJava.builder.ClsWrapperStubPsiFactory
import org.jetbrains.kotlin.asJava.builder.LightClassDataHolder
import org.jetbrains.kotlin.asJava.classes.FakeLightClassForFileOfPackage
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil
import org.jetbrains.kotlin.fileClasses.javaFileFacadeFqName
import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.ClsJavaStubByVirtualFileCache
import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.KtLightClassForDecompiledDeclaration
import org.jetbrains.kotlin.idea.caches.resolve.lightClasses.*
import org.jetbrains.kotlin.idea.decompiler.classFile.KtClsFile
import org.jetbrains.kotlin.idea.decompiler.navigation.SourceNavigationHelper
import org.jetbrains.kotlin.idea.project.ResolveElementCache
import org.jetbrains.kotlin.idea.stubindex.*
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope.Companion.sourceAndClassFiles
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
import org.jetbrains.kotlin.resolve.lazy.NoDescriptorForDeclarationException
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.utils.sure
import java.util.*
@@ -64,72 +57,36 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
private val scopeFileComparator = JavaElementFinder.byClasspathComparator(GlobalSearchScope.allScope(project))
private val psiManager: PsiManager = PsiManager.getInstance(project)
override fun createLightClassDataHolderForClassOrObject(classOrObject: KtClassOrObject, build: (LightClassConstructionContext) -> LightClassBuilderResult): LightClassDataHolder {
val (stub, bindingContext, diagnostics) = build(getContextForClassOrObject(classOrObject))
bindingContext.get(BindingContext.CLASS, classOrObject) ?: return InvalidLightClassDataHolder
return LightClassDataHolderImpl(
stub,
diagnostics
)
}
fun getContextForClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext {
if (classOrObject.isLocal) {
return getContextForLocalClassOrObject(classOrObject)
override fun createLightClassDataHolderForClassOrObject(
classOrObject: KtClassOrObject, builder: LightClassBuilder
): LightClassDataHolder {
return if (classOrObject.isLocal) {
LazyLightClassDataHolder(
builder,
exactContextProvider = { IDELightClassContexts.contextForLocalClassOrObject(classOrObject) },
dummyContextProvider = null
)
}
else {
return getContextForNonLocalClassOrObject(classOrObject)
LazyLightClassDataHolder(
builder,
exactContextProvider = { IDELightClassContexts.contextForNonLocalClassOrObject(classOrObject) },
dummyContextProvider = { IDELightClassContexts.lightContextForClassOrObject(classOrObject) }
)
}
}
private fun getContextForNonLocalClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext {
val resolutionFacade = classOrObject.getResolutionFacade()
val bindingContext = if (classOrObject is KtClass && classOrObject.isAnnotation()) {
// 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
resolutionFacade.getFrontendService(ResolveElementCache::class.java)
.resolvePrimaryConstructorParametersDefaultValues(classOrObject)
}
else {
resolutionFacade.analyze(classOrObject)
}
val classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject).sure {
"Class descriptor was not found for ${classOrObject.getElementTextWithContext()}"
}
ForceResolveUtil.forceResolveAllContents(classDescriptor)
return LightClassConstructionContext(bindingContext, resolutionFacade.moduleDescriptor)
}
private fun getContextForLocalClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext {
val resolutionFacade = classOrObject.getResolutionFacade()
val bindingContext = resolutionFacade.analyze(classOrObject)
val descriptor = bindingContext.get(BindingContext.CLASS, classOrObject)
if (descriptor == null) {
LOG.warn("No class descriptor in context for class: " + classOrObject.getElementTextWithContext())
return LightClassConstructionContext(bindingContext, resolutionFacade.moduleDescriptor)
}
ForceResolveUtil.forceResolveAllContents<ClassDescriptor>(descriptor)
return LightClassConstructionContext(bindingContext, resolutionFacade.moduleDescriptor)
}
override fun createLightClassDataHolderForFacade(files: Collection<KtFile>, build: (LightClassConstructionContext) -> LightClassBuilderResult): LightClassDataHolder {
val (stub, _, diagnostics) = build(getContextForFacade(files))
return LightClassDataHolderImpl(stub, diagnostics)
}
fun getContextForFacade(files: Collection<KtFile>): LightClassConstructionContext {
override fun createLightClassDataHolderForFacade(files: Collection<KtFile>, builder: LightClassBuilder): LightClassDataHolder {
assert(!files.isEmpty()) { "No files in facade" }
val sortedFiles = files.sortedWith(scopeFileComparator)
val file = sortedFiles.first()
val resolveSession = file.getResolutionFacade().getFrontendService(ResolveSession::class.java)
forceResolvePackageDeclarations(files, resolveSession)
return LightClassConstructionContext(resolveSession.bindingContext, resolveSession.moduleDescriptor)
return LazyLightClassDataHolder(
builder,
exactContextProvider = { IDELightClassContexts.contextForFacade(sortedFiles) },
dummyContextProvider = { IDELightClassContexts.lightContextForFacade(sortedFiles) }
)
}
override fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<KtClassOrObject> {
@@ -267,48 +224,6 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
}
}
private fun forceResolvePackageDeclarations(files: Collection<KtFile>, session: ResolveSession) {
for (file in files) {
if (file.isScript) continue
val packageFqName = file.packageFqName
// make sure we create a package descriptor
val packageDescriptor = session.moduleDescriptor.getPackage(packageFqName)
if (packageDescriptor.isEmpty()) {
LOG.warn("No descriptor found for package " + packageFqName + " in file " + file.name + "\n" + file.text)
session.forceResolveAll()
continue
}
for (declaration in file.declarations) {
if (declaration is KtFunction) {
val name = declaration.nameAsSafeName
val functions = packageDescriptor.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_IDE)
for (descriptor in functions) {
ForceResolveUtil.forceResolveAllContents(descriptor)
}
}
else if (declaration is KtProperty) {
val name = declaration.nameAsSafeName
val properties = packageDescriptor.memberScope.getContributedVariables(name, NoLookupLocation.FROM_IDE)
for (descriptor in properties) {
ForceResolveUtil.forceResolveAllContents(descriptor)
}
}
else if (declaration is KtClassOrObject || declaration is KtTypeAlias || declaration is KtDestructuringDeclaration) {
// Do nothing: we are not interested in classes or type aliases,
// and all destructuring declarations are erroneous at top level
}
else {
LOG.error("Unsupported declaration kind: " + declaration + " in file " + file.name + "\n" + file.text)
}
}
ForceResolveUtil.forceResolveAllContents(session.getFileAnnotations(file))
}
}
private fun getLightClassForDecompiledClassOrObject(decompiledClassOrObject: KtClassOrObject): KtLightClassForDecompiledDeclaration? {
if (decompiledClassOrObject is KtEnumEntry) {
return null
@@ -386,10 +301,6 @@ class IDELightClassGenerationSupport(private val project: Project) : LightClassG
javaFileStub.psi = fakeFile
return fakeFile.classes.single() as ClsClassImpl
}
companion object {
private val LOG = Logger.getInstance(IDELightClassGenerationSupport::class.java)
}
}
class KtFileClassProviderImpl(val lightClassGenerationSupport: LightClassGenerationSupport) : KtFileClassProvider {
@@ -0,0 +1,324 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.caches.resolve.lightClasses
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analyzer.LanguageSettingsProvider
import org.jetbrains.kotlin.asJava.builder.LightClassConstructionContext
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.container.useImpl
import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.context.LazyResolveToken
import org.jetbrains.kotlin.context.ModuleContext
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.frontend.di.configureModule
import org.jetbrains.kotlin.idea.caches.resolve.getModuleInfo
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.project.IdeaEnvironment
import org.jetbrains.kotlin.idea.project.ResolveElementCache
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.calls.CallResolver
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.resolve.lazy.FileScopeProviderImpl
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.WrappedTypeFactory
import org.jetbrains.kotlin.utils.sure
object IDELightClassContexts {
private val LOG = Logger.getInstance(this::class.java)
fun contextForNonLocalClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext {
val resolutionFacade = classOrObject.getResolutionFacade()
val bindingContext = if (classOrObject is KtClass && classOrObject.isAnnotation()) {
// 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
resolutionFacade.getFrontendService(ResolveElementCache::class.java)
.resolvePrimaryConstructorParametersDefaultValues(classOrObject)
}
else {
resolutionFacade.analyze(classOrObject)
}
val classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject).sure {
"Class descriptor was not found for ${classOrObject.getElementTextWithContext()}"
}
ForceResolveUtil.forceResolveAllContents(classDescriptor)
return LightClassConstructionContext(bindingContext, resolutionFacade.moduleDescriptor)
}
fun contextForLocalClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext {
val resolutionFacade = classOrObject.getResolutionFacade()
val bindingContext = resolutionFacade.analyze(classOrObject)
val descriptor = bindingContext.get(BindingContext.CLASS, classOrObject)
if (descriptor == null) {
LOG.warn("No class descriptor in context for class: " + classOrObject.getElementTextWithContext())
return LightClassConstructionContext(bindingContext, resolutionFacade.moduleDescriptor)
}
ForceResolveUtil.forceResolveAllContents(descriptor)
return LightClassConstructionContext(bindingContext, resolutionFacade.moduleDescriptor)
}
fun contextForFacade(files: List<KtFile>): LightClassConstructionContext {
val resolveSession = files.first().getResolutionFacade().getFrontendService(ResolveSession::class.java)
forceResolvePackageDeclarations(files, resolveSession)
return LightClassConstructionContext(resolveSession.bindingContext, resolveSession.moduleDescriptor)
}
fun lightContextForClassOrObject(classOrObject: KtClassOrObject): LightClassConstructionContext {
val resolveSession = setupAdHocResolve(classOrObject.project, classOrObject.getResolutionFacade().moduleDescriptor, listOf(classOrObject.containingKtFile))
ForceResolveUtil.forceResolveAllContents(resolveSession.resolveToDescriptor(classOrObject))
return LightClassConstructionContext(resolveSession.bindingContext, resolveSession.moduleDescriptor)
}
fun lightContextForFacade(files: List<KtFile>): LightClassConstructionContext {
val representativeFile = files.first()
val resolveSession = setupAdHocResolve(representativeFile.project, representativeFile.getResolutionFacade().moduleDescriptor, files)
forceResolvePackageDeclarations(files, resolveSession)
return LightClassConstructionContext(resolveSession.bindingContext, resolveSession.moduleDescriptor)
}
fun forceResolvePackageDeclarations(files: Collection<KtFile>, session: ResolveSession) {
for (file in files) {
if (file.isScript) continue
val packageFqName = file.packageFqName
// make sure we create a package descriptor
val packageDescriptor = session.moduleDescriptor.getPackage(packageFqName)
if (packageDescriptor.isEmpty()) {
LOG.warn("No descriptor found for package " + packageFqName + " in file " + file.name + "\n" + file.text)
session.forceResolveAll()
continue
}
for (declaration in file.declarations) {
if (declaration is KtFunction) {
val name = declaration.nameAsSafeName
val functions = packageDescriptor.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_IDE)
for (descriptor in functions) {
ForceResolveUtil.forceResolveAllContents(descriptor)
}
}
else if (declaration is KtProperty) {
val name = declaration.nameAsSafeName
val properties = packageDescriptor.memberScope.getContributedVariables(name, NoLookupLocation.FROM_IDE)
for (descriptor in properties) {
ForceResolveUtil.forceResolveAllContents(descriptor)
}
}
else if (declaration is KtClassOrObject || declaration is KtTypeAlias || declaration is KtDestructuringDeclaration) {
// Do nothing: we are not interested in classes or type aliases,
// and all destructuring declarations are erroneous at top level
}
else {
LOG.error("Unsupported declaration kind: " + declaration + " in file " + file.name + "\n" + file.text)
}
}
ForceResolveUtil.forceResolveAllContents(session.getFileAnnotations(file))
}
}
private fun setupAdHocResolve(project: Project, realWorldModule: ModuleDescriptor, files: List<KtFile>): ResolveSession {
val trace = BindingTraceContext()
val sm = LockBasedStorageManager.NO_LOCKS
val moduleDescriptor = ModuleDescriptorImpl(realWorldModule.name, sm, realWorldModule.builtIns)
setupDependencies(moduleDescriptor, realWorldModule)
val moduleInfo = files.first().getModuleInfo()
val container = createContainer("LightClassStub", JvmPlatform) {
val jvmTarget = LanguageSettingsProvider.getInstance(project).getTargetPlatform(moduleInfo) as? JvmTarget
configureModule(
ModuleContext(moduleDescriptor, project), JvmPlatform,
jvmTarget ?: JvmTarget.DEFAULT, trace
)
useInstance(GlobalSearchScope.EMPTY_SCOPE)
useInstance(LookupTracker.DO_NOTHING)
useImpl<FileScopeProviderImpl>()
useInstance(LanguageSettingsProvider.getInstance(project).getLanguageVersionSettings(moduleInfo, project))
useInstance(FileBasedDeclarationProviderFactory(sm, files))
useImpl<AdHocAnnotationResolver>()
useInstance(object : WrappedTypeFactory(sm) {
override fun createLazyWrappedType(computation: () -> KotlinType): KotlinType = errorType()
override fun createDeferredType(trace: BindingTrace, computation: () -> KotlinType) = errorType()
override fun createRecursionIntolerantDeferredType(trace: BindingTrace, computation: () -> KotlinType) = errorType()
private fun errorType() = ErrorUtils.createErrorType("Error type in ad hoc resolve for lighter classes")
})
IdeaEnvironment.configure(this)
useImpl<LazyResolveToken>()
useImpl<ResolveSession>()
}
val resolveSession = container.get<ResolveSession>()
moduleDescriptor.initialize(CompositePackageFragmentProvider(listOf(resolveSession.packageFragmentProvider)))
return resolveSession
}
private fun setupDependencies(moduleDescriptor: ModuleDescriptorImpl, realWorldModule: ModuleDescriptor) {
val jvmFieldClass = realWorldModule.getPackage(FqName("kotlin.jvm")).memberScope
.getContributedClassifier(Name.identifier("JvmField"), NoLookupLocation.FROM_IDE)
if (jvmFieldClass != null) {
moduleDescriptor.setDependencies(moduleDescriptor, jvmFieldClass.module as ModuleDescriptorImpl, moduleDescriptor.builtIns.builtInsModule)
}
else {
moduleDescriptor.setDependencies(moduleDescriptor, moduleDescriptor.builtIns.builtInsModule)
}
}
// see JvmPlatformAnnotations.kt, JvmFlagAnnotations.kt, also PsiModifier.MODIFIERS
private val annotationsThatAffectCodegen = listOf(
"JvmField", "JvmOverloads", "JvmName", "JvmStatic",
"Synchronized", "Transient", "Volatile", "Strictfp"
).map { FqName("kotlin.jvm").child(Name.identifier(it)) }
class AdHocAnnotationResolver(
private val moduleDescriptor: ModuleDescriptor,
callResolver: CallResolver,
constantExpressionEvaluator: ConstantExpressionEvaluator,
storageManager: StorageManager
) : AnnotationResolverImpl(callResolver, constantExpressionEvaluator, storageManager) {
override fun resolveAnnotationEntries(scope: LexicalScope, annotationEntries: List<KtAnnotationEntry>, trace: BindingTrace, shouldResolveArguments: Boolean): Annotations {
return super.resolveAnnotationEntries(scope, annotationEntries, trace, shouldResolveArguments)
}
override fun resolveAnnotationType(scope: LexicalScope, entryElement: KtAnnotationEntry, trace: BindingTrace): KotlinType {
return annotationClassByEntry(entryElement)?.defaultType ?: super.resolveAnnotationType(scope, entryElement, trace)
}
private fun annotationClassByEntry(entryElement: KtAnnotationEntry): ClassDescriptor? {
val annotationTypeReferencePsi = (entryElement.typeReference?.typeElement as? KtUserType)?.referenceExpression ?: return null
val referencedName = annotationTypeReferencePsi.getReferencedName()
for (annotationFqName in annotationsThatAffectCodegen) {
if (referencedName == annotationFqName.shortName().asString()) {
moduleDescriptor.getPackage(annotationFqName.parent()).memberScope
.getContributedClassifier(annotationFqName.shortName(), NoLookupLocation.FROM_IDE)?.let { return it as? ClassDescriptor }
}
}
return null
}
override fun resolveAnnotationCall(annotationEntry: KtAnnotationEntry, scope: LexicalScope, trace: BindingTrace): OverloadResolutionResults<FunctionDescriptor> {
val annotationConstructor = annotationClassByEntry(annotationEntry)?.constructors?.singleOrNull()
?: return super.resolveAnnotationCall(annotationEntry, scope, trace)
val valueArgumentText = valueArgumentText(annotationEntry)
?: return super.resolveAnnotationCall(annotationEntry, scope, trace)
val fakeResolvedCall = object : ResolvedCall<FunctionDescriptor> {
override fun getStatus() = ResolutionStatus.SUCCESS
override fun getCandidateDescriptor() = annotationConstructor
override fun getResultingDescriptor() = annotationConstructor
override fun getValueArguments() =
annotationConstructor.valueParameters.singleOrNull()?.let { mapOf(it to FakeResolvedValueArgument(valueArgumentText)) }.orEmpty()
override fun getCall() = notImplemented
override fun getExtensionReceiver() = notImplemented
override fun getDispatchReceiver() = notImplemented
override fun getExplicitReceiverKind() = notImplemented
override fun getValueArgumentsByIndex() = notImplemented
override fun getArgumentMapping(valueArgument: ValueArgument) = notImplemented
override fun getTypeArguments() = notImplemented
override fun getDataFlowInfoForArguments() = notImplemented
override fun getSmartCastDispatchReceiverType() = notImplemented
}
return object : OverloadResolutionResults<FunctionDescriptor> {
override fun isSingleResult() = true
override fun getResultingCall(): ResolvedCall<FunctionDescriptor> = fakeResolvedCall
override fun getResultingDescriptor() = annotationConstructor
override fun getAllCandidates() = notImplemented
override fun getResultingCalls() = notImplemented
override fun getResultCode() = notImplemented
override fun isSuccess() = notImplemented
override fun isNothing() = notImplemented
override fun isAmbiguity() = notImplemented
override fun isIncomplete() = notImplemented
}
}
private fun valueArgumentText(annotationEntry: KtAnnotationEntry) =
((annotationEntry.valueArguments.singleOrNull()?.getArgumentExpression() as? KtStringTemplateExpression)?.entries?.singleOrNull() as? KtLiteralStringTemplateEntry)?.text
override fun getAnnotationArgumentValue(trace: BindingTrace, valueParameter: ValueParameterDescriptor, resolvedArgument: ResolvedValueArgument): ConstantValue<*>? {
if (resolvedArgument is FakeResolvedValueArgument) return StringValue(resolvedArgument.argumentText, moduleDescriptor.builtIns)
return super.getAnnotationArgumentValue(trace, valueParameter, resolvedArgument)
}
private class FakeResolvedValueArgument(val argumentText: String) : ResolvedValueArgument {
override fun getArguments() = notImplemented
}
}
private val notImplemented: Nothing
get() = error("Should not be called")
}
@@ -0,0 +1,106 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.caches.resolve.lightClasses
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiClassType
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
import org.jetbrains.kotlin.asJava.builder.*
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightFieldImpl
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.elements.KtLightMethodImpl
import org.jetbrains.kotlin.psi.KtClassOrObject
typealias LightClassBuilder = (LightClassConstructionContext) -> LightClassBuilderResult
typealias LightClassContextProvider = () -> LightClassConstructionContext
class LazyLightClassDataHolder(
builder: LightClassBuilder,
exactContextProvider: LightClassContextProvider,
dummyContextProvider: LightClassContextProvider?
) : LightClassDataHolder {
private val exactResultLazyValue = lazy(LazyThreadSafetyMode.PUBLICATION) { builder(exactContextProvider()) }
private val exactResult: LightClassBuilderResult by exactResultLazyValue
private val lazyInexactResult by lazy(LazyThreadSafetyMode.PUBLICATION) {
dummyContextProvider?.let { builder.invoke(it()) }
}
private val inexactResult: LightClassBuilderResult?
get() = if (exactResultLazyValue.isInitialized()) null else lazyInexactResult
override val javaFileStub get() = exactResult.stub
override val extraDiagnostics get() = exactResult.diagnostics
// for facade or defaultImpls
override fun findData(findDelegate: (PsiJavaFileStub) -> PsiClass): LightClassData =
LazyLightClassData(relyOnDummySupertypes = true) { lightClassBuilderResult ->
findDelegate(lightClassBuilderResult.stub)
}
override fun findDataForClassOrObject(classOrObject: KtClassOrObject): LightClassData =
LazyLightClassData(relyOnDummySupertypes = classOrObject.getSuperTypeList() == null) { lightClassBuilderResult ->
lightClassBuilderResult.stub.findDelegate(classOrObject)
}
private inner class LazyLightClassData(
private val relyOnDummySupertypes: Boolean,
findDelegate: (LightClassBuilderResult) -> PsiClass
) : LightClassData {
override val clsDelegate: PsiClass by lazy(LazyThreadSafetyMode.PUBLICATION) { findDelegate(exactResult) }
private val dummyDelegate: PsiClass? by lazy(LazyThreadSafetyMode.PUBLICATION) { inexactResult?.let(findDelegate) }
override fun getOwnFields(containingClass: KtLightClass): List<KtLightField> {
if (dummyDelegate == null) return clsDelegate.fields.map { KtLightFieldImpl.fromClsField(it, containingClass) }
return dummyDelegate!!.fields.map { dummyField ->
val memberOrigin = ClsWrapperStubPsiFactory.getMemberOrigin(dummyField)!!
val fieldName = dummyField.name!!
KtLightFieldImpl.lazy(dummyField, memberOrigin, containingClass) {
clsDelegate.findFieldByName(fieldName, false)!!.apply {
assert(this.memberIndex!! == dummyField.memberIndex!!)
}
}
}
}
override fun getOwnMethods(containingClass: KtLightClass): List<KtLightMethod> {
if (dummyDelegate == null) return clsDelegate.methods.map { KtLightMethodImpl.fromClsMethod(it, containingClass) }
return dummyDelegate!!.methods.map { dummyMethod ->
val methodName = dummyMethod.name
KtLightMethodImpl.lazy(dummyMethod, containingClass, ClsWrapperStubPsiFactory.getMemberOrigin(dummyMethod)) {
val dummyIndex = dummyMethod.memberIndex!!
clsDelegate.findMethodsByName(methodName, false).filter {
delegateCandidate -> delegateCandidate.memberIndex == dummyIndex
}.single().apply {
assert(this.parameterList.parametersCount == dummyMethod.parameterList.parametersCount)
}
}
}
}
override val supertypes: Array<PsiClassType>
get() = if (relyOnDummySupertypes && dummyDelegate != null) dummyDelegate!!.superTypes else clsDelegate.superTypes
}
}
@@ -20,7 +20,10 @@ import com.intellij.psi.PsiCodeBlock
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiNameIdentifierOwner
import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.elements.isGetter
import org.jetbrains.kotlin.asJava.elements.isSetter
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.uast.*
@@ -30,9 +33,15 @@ import org.jetbrains.uast.kotlin.*
open class KotlinUMethod(
psi: KtLightMethod,
override val uastParent: UElement?
) : UMethod, JavaUElementWithComments, PsiMethod by psi {
) : UAnnotationMethod, JavaUElementWithComments, PsiMethod by psi {
override val psi: KtLightMethod = unwrap<UMethod, KtLightMethod>(psi)
override val uastDefaultValue by lz {
val annotationParameter = psi.kotlinOrigin as? KtParameter ?: return@lz null
val defaultValue = annotationParameter.defaultValue ?: return@lz null
getLanguagePlugin().convertElement(defaultValue, this) as? UExpression
}
private val kotlinOrigin = (psi.originalElement as KtLightElement<*, *>).kotlinOrigin
override val annotations by lz {
@@ -73,20 +82,6 @@ open class KotlinUMethod(
override fun hashCode() = psi.hashCode()
companion object {
fun create(psi: KtLightMethod, containingElement: UElement?) = when (psi) {
is KtLightMethodImpl.KtLightAnnotationMethod -> KotlinUAnnotationMethod(psi, containingElement)
else -> KotlinUMethod(psi, containingElement)
}
}
}
class KotlinUAnnotationMethod(
override val psi: KtLightMethodImpl.KtLightAnnotationMethod,
containingElement: UElement?
) : KotlinUMethod(psi, containingElement), UAnnotationMethod {
override val uastDefaultValue by lz {
val annotationParameter = psi.kotlinOrigin as? KtParameter ?: return@lz null
val defaultValue = annotationParameter.defaultValue ?: return@lz null
getLanguagePlugin().convertElement(defaultValue, this) as? UExpression
fun create(psi: KtLightMethod, containingElement: UElement?) = KotlinUMethod(psi, containingElement)
}
}
+1 -1
View File
@@ -6,4 +6,4 @@ class Foo {
val x = Foo::bar
// REF:Foo::bar
// RESULT:KtLightAnnotationMethod:bar
// RESULT:KtLightMethodImpl:bar
@@ -14,7 +14,7 @@ UFile (package = ) [public abstract interface Callback {...]
UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) [<init>()] : PsiType:UnsupportedOperationException
UIdentifier (Identifier (UnsupportedOperationException)) [UIdentifier (Identifier (UnsupportedOperationException))]
USimpleNameReferenceExpression (identifier = <init>) [<init>] : PsiType:UnsupportedOperationException
UMethod (name = Model) [public fun Model() {...}]
UAnnotationMethod (name = Model) [public fun Model() {...}]
UBlockExpression [{...}]
UBlockExpression [{...}] : PsiType:Unit
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 2)) [crashMe(Callback.java, { ...})] : PsiType:Unit