Refactor light members: introduce KtLightMemberImpl

Holds common code for fields and methods
This commit is contained in:
Pavel V. Talanov
2017-03-24 16:11:18 +03:00
parent a44aa8e112
commit 4b85fd9fbe
6 changed files with 118 additions and 131 deletions
@@ -16,13 +16,12 @@
package org.jetbrains.kotlin.asJava.elements
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiField
import com.intellij.psi.PsiNamedElement
import com.intellij.psi.*
import com.intellij.psi.impl.PsiVariableEx
import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
interface KtLightElement<out T : KtElement, out D : PsiElement> : PsiNamedElement {
val kotlinOrigin: T?
@@ -30,8 +29,16 @@ interface KtLightElement<out T : KtElement, out D : PsiElement> : PsiNamedElemen
val clsDelegate: D
}
interface KtLightDeclaration<out T: KtDeclaration, out D: PsiElement>: KtLightElement<T, D>
interface KtLightDeclaration<out T : KtDeclaration, out D : PsiElement> : KtLightElement<T, D>
interface KtLightField : PsiField, KtLightDeclaration<KtDeclaration, PsiField>, PsiVariableEx {
interface KtLightMember<out D : PsiMember> : PsiMember, KtLightElement<KtDeclaration, D>, PsiNameIdentifierOwner, PsiDocCommentOwner {
val lightMemberOrigin: LightMemberOrigin?
}
interface KtLightField : PsiField, KtLightMember<PsiField>, PsiVariableEx
interface KtLightMethod : PsiAnnotationMethod, KtLightMember<PsiMethod> {
val isDelegated: Boolean
get() = lightMemberOrigin?.originKind == JvmDeclarationOriginKind.DELEGATION
|| lightMemberOrigin?.originKind == JvmDeclarationOriginKind.DELEGATION_TO_DEFAULT_IMPLS
}
@@ -16,12 +16,8 @@
package org.jetbrains.kotlin.asJava.elements
import com.intellij.navigation.ItemPresentation
import com.intellij.navigation.ItemPresentationProviders
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.impl.PsiVariableEx
import com.intellij.psi.impl.light.LightElement
import com.intellij.util.IncorrectOperationException
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.asJava.builder.ClsWrapperStubPsiFactory
@@ -30,42 +26,24 @@ import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForEnumEntry
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import java.lang.UnsupportedOperationException
// Copied from com.intellij.psi.impl.light.LightField
sealed class KtLightFieldImpl<T: PsiField>(
sealed class KtLightFieldImpl<D : PsiField>(
override val lightMemberOrigin: LightMemberOrigin?,
computeRealDelegate: () -> T,
private val containingClass: KtLightClass,
private val dummyDelegate: PsiField?
) : LightElement(containingClass.manager, KotlinLanguage.INSTANCE), KtLightField {
private val lightIdentifier by lazyPub { KtLightIdentifier(this, kotlinOrigin as? KtNamedDeclaration) }
computeRealDelegate: () -> D,
containingClass: KtLightClass,
dummyDelegate: PsiField?
) : KtLightMemberImpl<PsiField>(computeRealDelegate, lightMemberOrigin, containingClass, dummyDelegate), KtLightField {
override val clsDelegate: T by lazyPub(computeRealDelegate)
override val clsDelegate: D
@Suppress("UNCHECKED_CAST")
get() = super.clsDelegate as D
@Throws(IncorrectOperationException::class)
override fun setInitializer(initializer: PsiExpression?) = throw IncorrectOperationException("Not supported")
override fun getUseScope() = kotlinOrigin?.useScope ?: super.getUseScope()
override fun getPresentation(): ItemPresentation? = (kotlinOrigin ?: this).let { ItemPresentationProviders.getItemPresentation(it) }
override fun getName() = dummyDelegate?.name ?: clsDelegate.name
override fun getNameIdentifier() = lightIdentifier
override fun getDocComment() = clsDelegate.docComment
override fun isDeprecated() = clsDelegate.isDeprecated
override fun getContainingClass() = containingClass
override fun getContainingFile() = containingClass.containingFile
override fun getType() = clsDelegate.type
override fun getTypeElement() = clsDelegate.typeElement
@@ -84,34 +62,12 @@ sealed class KtLightFieldImpl<T: PsiField>(
return this
}
private val _modifierList by lazyPub {
if (lightMemberOrigin is LightMemberOriginForDeclaration)
clsDelegate.modifierList?.let { KtLightModifierList(it, this) }
else clsDelegate.modifierList
}
override fun getModifierList() = _modifierList
override fun hasModifierProperty(@NonNls name: String) = (dummyDelegate ?: clsDelegate).hasModifierProperty(name)
override fun getText() = kotlinOrigin?.text ?: ""
override fun getTextRange() = kotlinOrigin?.textRange ?: TextRange.EMPTY_RANGE
override fun isValid() = containingClass.isValid
override fun toString(): String = "${this::class.java.simpleName}:$name"
override fun equals(other: Any?): Boolean =
other is KtLightFieldImpl<*> &&
this.name == other.name &&
this.containingClass == other.containingClass
override fun hashCode() = 31 * containingClass.hashCode() + (name?.hashCode() ?: 0)
override val kotlinOrigin: KtDeclaration? get() = lightMemberOrigin?.originalElement
override fun getNavigationElement() = kotlinOrigin ?: super.getNavigationElement()
override fun hashCode() = 31 * containingClass.hashCode() + name.hashCode()
override fun computeConstantValue(visitedVars: MutableSet<PsiVariable>?): Any? {
return (clsDelegate as PsiVariableEx).computeConstantValue(visitedVars)
@@ -124,8 +80,6 @@ sealed class KtLightFieldImpl<T: PsiField>(
return super.isEquivalentTo(another)
}
override fun isWritable() = kotlinOrigin?.isWritable ?: false
override fun copy() = Factory.create(lightMemberOrigin?.copy(), clsDelegate, containingClass)
@@ -134,7 +88,7 @@ sealed class KtLightFieldImpl<T: PsiField>(
computeDelegate: () -> PsiEnumConstant,
containingClass: KtLightClass,
dummyDelegate: PsiField?
) : KtLightFieldImpl<PsiEnumConstant>(origin, computeDelegate , containingClass, dummyDelegate), PsiEnumConstant {
) : KtLightFieldImpl<PsiEnumConstant>(origin, computeDelegate, containingClass, dummyDelegate), PsiEnumConstant {
private val initializingClass by lazyPub {
val kotlinEnumEntry = (lightMemberOrigin as? LightMemberOriginForDeclaration)?.originalElement as? KtEnumEntry
if (kotlinEnumEntry != null && kotlinEnumEntry.declarations.isNotEmpty()) {
@@ -0,0 +1,82 @@
/*
* 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.elements
import com.intellij.navigation.ItemPresentation
import com.intellij.navigation.ItemPresentationProviders
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.impl.light.LightElement
import org.jetbrains.kotlin.asJava.builder.LightMemberOrigin
import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtNamedDeclaration
abstract class KtLightMemberImpl<out D : PsiMember>(
computeRealDelegate: () -> D,
override val lightMemberOrigin: LightMemberOrigin?,
private val containingClass: KtLightClass,
private val dummyDelegate: D?
) : LightElement(containingClass.manager, KotlinLanguage.INSTANCE), PsiMember, KtLightMember<D> {
override val clsDelegate by lazyPub(computeRealDelegate)
private val lightIdentifier by lazyPub { KtLightIdentifier(this, kotlinOrigin as? KtNamedDeclaration) }
private val _modifierList by lazyPub {
if (lightMemberOrigin is LightMemberOriginForDeclaration)
clsDelegate.modifierList?.let { KtLightModifierList(it, this) }
else clsDelegate.modifierList
}
override fun hasModifierProperty(name: String) = (dummyDelegate ?: clsDelegate).hasModifierProperty(name)
override fun getModifierList(): PsiModifierList? = _modifierList
override fun toString(): String = "${this::class.java.simpleName}:$name"
override fun getContainingClass() = containingClass
override fun getContainingFile() = containingClass.containingFile
override fun getParent(): PsiElement = containingClass
override fun isValid() = containingClass.isValid
override fun getName(): String = dummyDelegate?.name ?: clsDelegate.name!!
override fun getNameIdentifier(): PsiIdentifier = lightIdentifier
override fun getUseScope() = kotlinOrigin?.useScope ?: super.getUseScope()
override val kotlinOrigin: KtDeclaration? get() = lightMemberOrigin?.originalElement
override fun getNavigationElement() = kotlinOrigin ?: super.getNavigationElement()
override fun getPresentation(): ItemPresentation? = (kotlinOrigin ?: this).let { ItemPresentationProviders.getItemPresentation(it) }
override fun getText() = kotlinOrigin?.text ?: ""
override fun getTextRange() = kotlinOrigin?.textRange ?: TextRange.EMPTY_RANGE
override fun isWritable() = kotlinOrigin?.isWritable ?: false
override fun getDocComment() = (clsDelegate as PsiDocCommentOwner).docComment
override fun isDeprecated() = (clsDelegate as PsiDocCommentOwner).isDeprecated
}
@@ -16,13 +16,8 @@
package org.jetbrains.kotlin.asJava.elements
import com.intellij.core.JavaCoreBundle
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.LightElement
import com.intellij.psi.javadoc.PsiDocComment
import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.psi.util.*
import com.intellij.util.IncorrectOperationException
@@ -32,29 +27,17 @@ import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.lazyPub
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 : PsiAnnotationMethod, KtLightDeclaration<KtDeclaration, PsiMethod> {
val lightMethodOrigin: LightMemberOrigin?
val isDelegated: Boolean
}
class KtLightMethodImpl private constructor(
computeRealDelegate: () -> PsiMethod,
override val lightMethodOrigin: LightMemberOrigin?,
private val containingClass: KtLightClass,
lightMemberOrigin: LightMemberOrigin?,
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 lazyPub(computeRealDelegate)
private val lightIdentifier by lazyPub { KtLightIdentifier(this, kotlinOrigin as? KtNamedDeclaration) }
) : KtLightMemberImpl<PsiMethod>(computeRealDelegate, lightMemberOrigin, containingClass, dummyDelegate), KtLightMethod {
private val returnTypeElem by lazyPub {
val delegateTypeElement = clsDelegate.returnTypeElement as? ClsTypeElementImpl
delegateTypeElement?.let { ClsTypeElementImpl(this, it.canonicalText, /*ClsTypeElementImpl.VARIANCE_NONE */ 0.toChar()) }
@@ -62,8 +45,6 @@ class KtLightMethodImpl private constructor(
private val calculatingReturnType = ThreadLocal<Boolean>()
override fun getContainingClass(): KtLightClass = containingClass
private val paramsList: PsiParameterList by lazyPub {
KtLightParameterList(this, dummyDelegate?.parameterList?.parametersCount ?: clsDelegate.parameterList.parametersCount) {
clsDelegate.parameterList.parameters.mapIndexed { index, clsParameter -> KtLightParameter(clsParameter, index, this@KtLightMethodImpl) }
@@ -74,7 +55,7 @@ class KtLightMethodImpl private constructor(
val cacheManager = CachedValuesManager.getManager(clsDelegate.project)
cacheManager.createCachedValue<PsiTypeParameterList>(
{
val origin = (lightMethodOrigin as? LightMemberOriginForDeclaration)?.originalElement
val origin = (lightMemberOrigin as? LightMemberOriginForDeclaration)?.originalElement
val list = if (origin != null) {
if (origin is KtClassOrObject) {
KotlinLightTypeParameterListBuilder(manager)
@@ -91,16 +72,6 @@ class KtLightMethodImpl private constructor(
)
}
override fun getNavigationElement(): PsiElement = kotlinOrigin?.navigationElement ?: super.getNavigationElement()
override fun getPresentation(): ItemPresentation? = kotlinOrigin?.presentation ?: super.getPresentation()
override fun getParent(): PsiElement = containingClass
override fun getText() = kotlinOrigin?.text ?: ""
override fun getTextRange() = kotlinOrigin?.textRange ?: TextRange.EMPTY_RANGE
override val isDelegated: Boolean
get() = lightMethodOrigin?.originKind == JvmDeclarationOriginKind.DELEGATION
|| lightMethodOrigin?.originKind == JvmDeclarationOriginKind.DELEGATION_TO_DEFAULT_IMPLS
override fun accept(visitor: PsiElementVisitor) {
if (visitor is JavaElementVisitor) {
visitor.visitMethod(this)
@@ -136,31 +107,22 @@ class KtLightMethodImpl private constructor(
} ?: throwCanNotModify()
}
private fun throwCanNotModify(): Nothing {
throw IncorrectOperationException(JavaCoreBundle.message("psi.error.attempt.to.edit.class.file"))
}
private val _modifierList by lazyPub {
if (lightMethodOrigin is LightMemberOriginForDeclaration)
KtLightModifierList(clsDelegate.modifierList, this)
else clsDelegate.modifierList
}
// TODO: add message
private fun throwCanNotModify(): Nothing = throw IncorrectOperationException()
override fun getModifierList(): PsiModifierList {
if (calculatingReturnType.get() == true) {
return KotlinJavaPsiFacade.getInstance(project).emptyModifierList
}
return _modifierList
return super.getModifierList()!!
}
override fun getNameIdentifier() = lightIdentifier
override fun getParameterList() = paramsList
override fun getTypeParameterList() = typeParamsList.value
override fun getTypeParameters(): Array<PsiTypeParameter> =
typeParameterList?.let { it.typeParameters } ?: PsiTypeParameter.EMPTY_ARRAY
typeParameterList?.typeParameters ?: PsiTypeParameter.EMPTY_ARRAY
override fun getSignature(substitutor: PsiSubstitutor): MethodSignature {
if (substitutor == PsiSubstitutor.EMPTY) {
@@ -170,13 +132,9 @@ class KtLightMethodImpl private constructor(
}
override fun copy(): PsiElement {
return Factory.create(clsDelegate, lightMethodOrigin?.copy(), containingClass)
return Factory.create(clsDelegate, lightMemberOrigin?.copy(), containingClass)
}
override fun getUseScope() = kotlinOrigin?.useScope ?: super.getUseScope()
override fun getLanguage() = KotlinLanguage.INSTANCE
override fun processDeclarations(processor: PsiScopeProcessor, state: ResolveState, lastParent: PsiElement?, place: PsiElement): Boolean {
return typeParameters.all { processor.execute(it, state) }
}
@@ -200,12 +158,10 @@ class KtLightMethodImpl private constructor(
other is KtLightMethodImpl &&
this.name == other.name &&
this.containingClass == other.containingClass &&
this.lightMethodOrigin == other.lightMethodOrigin &&
this.lightMemberOrigin == other.lightMemberOrigin &&
this._memberIndex == other._memberIndex
override fun hashCode(): Int = ((getName().hashCode() * 31 + (lightMethodOrigin?.hashCode() ?: 0)) * 31 + containingClass.hashCode()) * 31 + (_memberIndex?.hashCode() ?: 0)
override fun toString(): String = "${this::class.java.simpleName}:$name"
override fun hashCode(): Int = ((getName().hashCode() * 31 + (lightMemberOrigin?.hashCode() ?: 0)) * 31 + containingClass.hashCode()) * 31 + (_memberIndex?.hashCode() ?: 0)
override fun getDefaultValue() = (clsDelegate as? PsiAnnotationMethod)?.defaultValue
@@ -253,10 +209,6 @@ class KtLightMethodImpl private constructor(
}
}
override fun getName() = dummyDelegate?.name ?: clsDelegate.name
override fun hasModifierProperty(name: String) = (dummyDelegate ?: clsDelegate).hasModifierProperty(name)
override fun getThrowsList() = clsDelegate.throwsList
override fun hasTypeParameters() = clsDelegate.hasTypeParameters()
@@ -267,14 +219,10 @@ class KtLightMethodImpl private constructor(
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()
@@ -284,10 +232,6 @@ class KtLightMethodImpl private constructor(
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 {
@@ -53,7 +53,7 @@ public class KtLightParameter extends LightParameter implements KtLightDeclarati
this.index = index;
this.method = method;
if (method.getLightMethodOrigin() instanceof LightMemberOriginForDeclaration) {
if (method.getLightMemberOrigin() instanceof LightMemberOriginForDeclaration) {
this.modifierList = new KtLightModifierListWithExplicitModifiers(this, ArrayUtil.EMPTY_STRING_ARRAY) {
@Override
public PsiAnnotationOwner getDelegate() {
@@ -105,7 +105,7 @@ class KotlinShortNamesCacheTest : KotlinCodeInsightTestCase() {
= a.map { "${(it as KtLightMethod).clsDelegate.getKotlinFqName()} static=${it.hasModifierProperty(PsiModifier.STATIC)}" }.joinToString("\n")
fun accessorArrayDebugToString(a: Array<PsiMethod>)
= a.map { "${(it as KtLightMethod).clsDelegate.getKotlinFqName()} property=${(it.lightMethodOrigin?.originalElement as KtProperty).fqName} static=${it.hasModifierProperty(PsiModifier.STATIC)}" }.joinToString("\n")
= a.map { "${(it as KtLightMethod).clsDelegate.getKotlinFqName()} property=${(it.lightMemberOrigin?.originalElement as KtProperty).fqName} static=${it.hasModifierProperty(PsiModifier.STATIC)}" }.joinToString("\n")
fun checkMethodFound(methods: Array<PsiMethod>, stringFqName: String, static: Boolean) {
assertNotNull("Method $stringFqName with static=$static not found\n" + methodArrayDebugToString(methods),
@@ -191,7 +191,7 @@ class KotlinShortNamesCacheTest : KotlinCodeInsightTestCase() {
methods.find {
stringFqName == (it as KtLightMethod).clsDelegate.getKotlinFqName().toString()
&&
(it.lightMethodOrigin?.originalElement as KtProperty).fqName?.asString() == propertyFqName
(it.lightMemberOrigin?.originalElement as KtProperty).fqName?.asString() == propertyFqName
&&
it.hasModifierProperty(PsiModifier.STATIC) == static
})