[Analysis API] Add KtFe10ReferenceResolutionHelper and ...

Make implementation of KtFe10XXXReference follow the old
descriptor-based implementation as closely as possible.
This commit is contained in:
Tianyu Geng
2022-05-04 16:59:40 +00:00
committed by Ilya Kirillov
parent b5139c958b
commit 0803ab0d0b
14 changed files with 430 additions and 130 deletions
@@ -6,22 +6,21 @@
package org.jetbrains.kotlin.analysis.api.descriptors.references
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.Fe10AnalysisFacade.AnalysisMode
import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.references.base.KtFe10Reference
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.toKtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.references.KtArrayAccessReference
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.psi.KtImportAlias
import org.jetbrains.kotlin.resolve.BindingContext
class KtFe10ArrayAccessReference(expression: KtArrayAccessExpression) : KtArrayAccessReference(expression), KtFe10Reference {
override fun KtAnalysisSession.resolveToSymbols(): Collection<KtSymbol> {
require(this is KtFe10AnalysisSession)
override fun isReferenceToImportAlias(alias: KtImportAlias): Boolean {
return super<KtFe10Reference>.isReferenceToImportAlias(alias)
}
val bindingContext = analysisContext.analyze(expression, AnalysisMode.PARTIAL)
val descriptor = expression.getResolvedCall(bindingContext)?.resultingDescriptor
return listOfNotNull(descriptor?.toKtCallableSymbol(analysisContext))
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val getFunctionDescriptor = context[BindingContext.INDEXED_LVALUE_GET, expression]?.candidateDescriptor
val setFunctionDescriptor = context[BindingContext.INDEXED_LVALUE_SET, expression]?.candidateDescriptor
return listOfNotNull(getFunctionDescriptor, setFunctionDescriptor)
}
}
@@ -5,24 +5,22 @@
package org.jetbrains.kotlin.analysis.api.descriptors.references
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.Fe10AnalysisFacade.AnalysisMode
import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.references.base.KtFe10Reference
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.toKtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.references.KtCollectionLiteralReference
import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression
import org.jetbrains.kotlin.psi.KtImportAlias
import org.jetbrains.kotlin.resolve.BindingContext
class KtFe10CollectionLiteralReference(
expression: KtCollectionLiteralExpression
) : KtCollectionLiteralReference(expression), KtFe10Reference {
override fun KtAnalysisSession.resolveToSymbols(): Collection<KtSymbol> {
require(this is KtFe10AnalysisSession)
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val resolvedCall = context[BindingContext.COLLECTION_LITERAL_CALL, element]
return listOfNotNull(resolvedCall?.resultingDescriptor)
}
val bindingContext = analysisContext.analyze(expression, AnalysisMode.PARTIAL)
val descriptor = bindingContext[BindingContext.COLLECTION_LITERAL_CALL, expression]?.resultingDescriptor
return listOfNotNull(descriptor?.toKtCallableSymbol(analysisContext))
override fun isReferenceToImportAlias(alias: KtImportAlias): Boolean {
return super<KtFe10Reference>.isReferenceToImportAlias(alias)
}
}
@@ -5,24 +5,19 @@
package org.jetbrains.kotlin.analysis.api.descriptors.references
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.Fe10AnalysisFacade.AnalysisMode
import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.references.base.KtFe10Reference
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.toKtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.idea.references.KtConstructorDelegationReference
import org.jetbrains.kotlin.psi.KtConstructorDelegationReferenceExpression
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.psi.KtImportAlias
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
class KtFe10ConstructorDelegationReference(
expression: KtConstructorDelegationReferenceExpression
) : KtConstructorDelegationReference(expression), KtFe10Reference {
override fun KtAnalysisSession.resolveToSymbols(): Collection<KtSymbol> {
require(this is KtFe10AnalysisSession)
override fun getTargetDescriptors(context: BindingContext) = expression.getReferenceTargets(context)
val bindingContext = analysisContext.analyze(expression, AnalysisMode.PARTIAL)
val descriptor = expression.getResolvedCall(bindingContext)?.resultingDescriptor
return listOfNotNull(descriptor?.toKtCallableSymbol(analysisContext))
override fun isReferenceToImportAlias(alias: KtImportAlias): Boolean {
return super<KtFe10Reference>.isReferenceToImportAlias(alias)
}
}
@@ -5,24 +5,42 @@
package org.jetbrains.kotlin.analysis.api.descriptors.references
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.Fe10AnalysisFacade.AnalysisMode
import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.analysis.api.descriptors.references.base.KtFe10Reference
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.toKtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.analysis.api.descriptors.references.base.KtFe10ReferenceResolutionHelper
import org.jetbrains.kotlin.analysis.api.tokens.HackToForceAllowRunningAnalyzeOnEDT
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference
import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry
import org.jetbrains.kotlin.psi.KtImportAlias
import org.jetbrains.kotlin.resolve.BindingContext
class KtFe10DestructuringDeclarationEntry(
element: KtDestructuringDeclarationEntry
) : KtDestructuringDeclarationReference(element), KtFe10Reference {
override fun KtAnalysisSession.resolveToSymbols(): Collection<KtSymbol> {
check(this is KtFe10AnalysisSession)
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
return listOfNotNull(
context[BindingContext.VARIABLE, element],
context[BindingContext.COMPONENT_RESOLVED_CALL, element]?.candidateDescriptor
)
}
val bindingContext = analysisContext.analyze(element, AnalysisMode.PARTIAL)
val descriptor = bindingContext[BindingContext.COMPONENT_RESOLVED_CALL, element]?.resultingDescriptor
return listOfNotNull(descriptor?.toKtCallableSymbol(analysisContext))
override fun resolve() = multiResolve(false).asSequence()
.map { it.element }
.firstOrNull { it is KtDestructuringDeclarationEntry }
override fun getRangeInElement() = TextRange(0, element.textLength)
override fun isReferenceToImportAlias(alias: KtImportAlias): Boolean {
return super<KtFe10Reference>.isReferenceToImportAlias(alias)
}
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
override fun canRename(): Boolean {
val bindingContext = KtFe10ReferenceResolutionHelper.getInstance().partialAnalyze(element) //TODO: should it use full body resolve?
return resolveToDescriptors(bindingContext).all {
it is CallableMemberDescriptor && it.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
}
}
}
@@ -5,26 +5,29 @@
package org.jetbrains.kotlin.analysis.api.descriptors.references
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.references.base.KtFe10Reference
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.toKtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.references.KtForLoopInReference
import org.jetbrains.kotlin.psi.KtForExpression
import org.jetbrains.kotlin.psi.KtImportAlias
import org.jetbrains.kotlin.resolve.BindingContext
class KtFe10ForLoopInReference(expression: KtForExpression) : KtForLoopInReference(expression), KtFe10Reference {
override fun KtAnalysisSession.resolveToSymbols(): Collection<KtSymbol> {
check(this is KtFe10AnalysisSession)
val loopRange = element.loopRange ?: return emptyList()
val bindingContext = analysisContext.analyze(loopRange)
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val loopRange = expression.loopRange ?: return emptyList()
return LOOP_RANGE_KEYS.mapNotNull { key -> context.get(key, loopRange)?.candidateDescriptor }
}
return listOf(
companion object {
private val LOOP_RANGE_KEYS = arrayOf(
BindingContext.LOOP_RANGE_ITERATOR_RESOLVED_CALL,
BindingContext.LOOP_RANGE_HAS_NEXT_RESOLVED_CALL,
BindingContext.LOOP_RANGE_NEXT_RESOLVED_CALL
).mapNotNull { slice -> bindingContext[slice, loopRange]?.resultingDescriptor?.toKtCallableSymbol(analysisContext) }
BindingContext.LOOP_RANGE_NEXT_RESOLVED_CALL,
BindingContext.LOOP_RANGE_HAS_NEXT_RESOLVED_CALL
)
}
override fun isReferenceToImportAlias(alias: KtImportAlias): Boolean {
return super<KtFe10Reference>.isReferenceToImportAlias(alias)
}
}
@@ -5,35 +5,33 @@
package org.jetbrains.kotlin.analysis.api.descriptors.references
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.Fe10AnalysisFacade.AnalysisMode
import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.references.base.KtFe10Reference
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.toKtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.references.KtInvokeFunctionReference
import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtImportAlias
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.resolve.calls.util.getCall
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
class KtFe10InvokeFunctionReference(expression: KtCallExpression) : KtInvokeFunctionReference(expression), KtFe10Reference {
override fun KtAnalysisSession.resolveToSymbols(): Collection<KtSymbol> {
require(this is KtFe10AnalysisSession)
val bindingContext = analysisContext.analyze(expression, AnalysisMode.PARTIAL)
val call = expression.getCall(bindingContext)
val resolvedCall = call.getResolvedCall(bindingContext)
val descriptors = when {
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val call = element.getCall(context)
val resolvedCall = call.getResolvedCall(context)
return when {
resolvedCall is VariableAsFunctionResolvedCall ->
setOf((resolvedCall as VariableAsFunctionResolvedCall).functionCall.resultingDescriptor)
setOf<DeclarationDescriptor>((resolvedCall as VariableAsFunctionResolvedCall).functionCall.candidateDescriptor)
call != null && resolvedCall != null && call.callType == Call.CallType.INVOKE ->
setOf(resolvedCall.resultingDescriptor)
setOf<DeclarationDescriptor>(resolvedCall.candidateDescriptor)
else ->
emptyList()
}
return descriptors.mapNotNull { it.toKtCallableSymbol(analysisContext) }
}
override fun isReferenceToImportAlias(alias: KtImportAlias): Boolean {
return super<KtFe10Reference>.isReferenceToImportAlias(alias)
}
}
@@ -5,39 +5,31 @@
package org.jetbrains.kotlin.analysis.api.descriptors.references
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.references.base.KtFe10Reference
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.toKtCallableSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
import org.jetbrains.kotlin.descriptors.accessors
import org.jetbrains.kotlin.idea.references.KtPropertyDelegationMethodsReference
import org.jetbrains.kotlin.psi.KtImportAlias
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtPropertyDelegate
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
class KtFe10PropertyDelegationMethodsReference(
expression: KtPropertyDelegate
) : KtPropertyDelegationMethodsReference(expression), KtFe10Reference {
override fun KtAnalysisSession.resolveToSymbols(): Collection<KtSymbol> {
require(this is KtFe10AnalysisSession)
val property = expression.parent as? KtProperty
if (property == null || property.delegate !== element) {
return emptyList()
}
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val property = expression.getStrictParentOfType<KtProperty>() ?: return emptyList()
val descriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, property] as? VariableDescriptorWithAccessors
?: return emptyList()
return descriptor.accessors.mapNotNull { accessor ->
context.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, accessor)?.candidateDescriptor
} + listOfNotNull(context.get(BindingContext.PROVIDE_DELEGATE_RESOLVED_CALL, descriptor)?.candidateDescriptor)
}
val bindingContext = analysisContext.analyze(property)
val propertyDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, property]
if (propertyDescriptor is PropertyDescriptor) {
return listOfNotNull(propertyDescriptor.getter, propertyDescriptor.setter)
.mapNotNull { accessor ->
val descriptor = bindingContext[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, accessor]?.resultingDescriptor
descriptor?.toKtCallableSymbol(analysisContext)
}
}
return emptyList()
override fun isReferenceToImportAlias(alias: KtImportAlias): Boolean {
return super<KtFe10Reference>.isReferenceToImportAlias(alias)
}
}
@@ -6,33 +6,80 @@
package org.jetbrains.kotlin.analysis.api.descriptors.references
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.Fe10AnalysisFacade.AnalysisMode
import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
import com.intellij.util.SmartList
import org.jetbrains.kotlin.analysis.api.descriptors.references.base.KtFe10Reference
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.toKtSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.analysis.api.descriptors.references.base.KtFe10ReferenceResolutionHelper
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.readWriteAccess
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
import org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension
import org.jetbrains.kotlin.psi.KtImportAlias
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor
import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
class KtFe10SimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleNameReference(expression), KtFe10Reference {
override fun KtAnalysisSession.resolveToSymbols(): Collection<KtSymbol> {
require(this is KtFe10AnalysisSession)
val bindingContext = analysisContext.analyze(expression, AnalysisMode.PARTIAL)
val descriptor = bindingContext[BindingContext.REFERENCE_TARGET, expression]
?: expression.getResolvedCall(bindingContext)?.resultingDescriptor
return listOfNotNull(descriptor?.toKtSymbol(analysisContext))
override fun canBeReferenceTo(candidateTarget: PsiElement): Boolean {
return element.containingFile == candidateTarget.containingFile ||
KtFe10ReferenceResolutionHelper.getInstance().isInProjectOrLibSource(element, includeScriptsOutsideSourceRoots = true)
}
override fun isReferenceToViaExtension(element: PsiElement): Boolean = false
override fun isReferenceToImportAlias(alias: KtImportAlias): Boolean {
return super<KtFe10Reference>.isReferenceToImportAlias(alias)
}
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
return SmartList<DeclarationDescriptor>().apply {
// Replace Java property with its accessor(s)
for (descriptor in expression.getReferenceTargets(context)) {
val sizeBefore = size
if (descriptor !is JavaPropertyDescriptor) {
add(descriptor)
continue
}
val readWriteAccess = expression.readWriteAccess(true)
descriptor.getter?.let {
if (readWriteAccess.isRead) add(it)
}
descriptor.setter?.let {
if (readWriteAccess.isWrite) add(it)
}
if (size == sizeBefore) {
add(descriptor)
}
}
}
}
override fun isReferenceToViaExtension(element: PsiElement): Boolean {
for (extension in element.project.extensionArea.getExtensionPoint(SimpleNameReferenceExtension.EP_NAME).extensions) {
if (extension.isReferenceTo(this, element)) return true
}
return false
}
override fun getImportAlias(): KtImportAlias? {
fun DeclarationDescriptor.unwrap() = if (this is ImportedFromObjectCallableDescriptor<*>) callableFromObject else this
val name = element.getReferencedName()
val file = element.containingKtFile
val importDirective = file.findImportByAlias(name) ?: return null
val fqName = importDirective.importedFqName ?: return null
val helper = KtFe10ReferenceResolutionHelper.getInstance()
val importedDescriptors = helper.resolveImportReference(file, fqName).map { it.unwrap() }
if (getTargetDescriptors(helper.partialAnalyze(element)).any {
it.unwrap().getImportableDescriptor() in importedDescriptors
}) {
return importDirective.alias
}
return null
}
}
@@ -5,38 +5,72 @@
package org.jetbrains.kotlin.analysis.api.descriptors.references.base
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementResolveResult
import com.intellij.psi.ResolveResult
import com.intellij.psi.impl.source.resolve.ResolveCache
import org.jetbrains.kotlin.analysis.api.KtAllowAnalysisOnEdt
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.analysis.api.descriptors.Fe10AnalysisFacade.AnalysisMode
import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
import org.jetbrains.kotlin.analysis.api.lifetime.allowAnalysisOnEdt
import org.jetbrains.kotlin.analysis.api.descriptors.utils.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
import org.jetbrains.kotlin.idea.references.AbstractKtReference
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.load.kotlin.toSourceElement
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.source.getPsi
import java.util.*
object KtFe10PolyVariantResolver : ResolveCache.PolyVariantResolver<KtReference> {
@OptIn(KtAllowAnalysisOnEdt::class)
override fun resolve(reference: KtReference, incompleteCode: Boolean): Array<ResolveResult> {
require(reference is AbstractKtReference<*>) { "reference should be AbstractKtReference, but was ${reference::class}" }
return allowAnalysisOnEdt {
val expression = reference.expression
analyze(reference.expression) {
val analysisSession = this as KtFe10AnalysisSession
val bindingContext = analysisSession.analysisContext.analyze(expression, AnalysisMode.PARTIAL)
val descriptor = when (expression) {
is KtReferenceExpression -> bindingContext[BindingContext.REFERENCE_TARGET, expression]
else -> expression.getResolvedCall(bindingContext)?.resultingDescriptor
}
val source = descriptor?.toSourceElement?.getPsi()
if (source != null) arrayOf(PsiElementResolveResult(source)) else emptyArray()
}
class KotlinResolveResult(element: PsiElement) : PsiElementResolveResult(element)
private fun resolveToPsiElements(ref: KtFe10Reference): Collection<PsiElement> {
require(ref is AbstractKtReference<*>) { "reference should be AbstractKtReference, but was ${ref::class}" }
val bindingContext = KtFe10ReferenceResolutionHelper.getInstance().partialAnalyze(ref.expression)
if (bindingContext == BindingContext.EMPTY) return emptySet()
return resolveToPsiElements(ref, bindingContext, ref.getTargetDescriptors(bindingContext))
}
private fun resolveToPsiElements(
ref: KtFe10Reference,
context: BindingContext,
targetDescriptors: Collection<DeclarationDescriptor>
): Collection<PsiElement> {
if (targetDescriptors.isNotEmpty()) {
return targetDescriptors.flatMap { target -> resolveToPsiElements(ref, target) }.toSet()
}
val labelTargets = getLabelTargets(ref, context)
if (labelTargets != null) {
return labelTargets
}
return Collections.emptySet()
}
private fun resolveToPsiElements(
ref: KtFe10Reference,
targetDescriptor: DeclarationDescriptor
): Collection<PsiElement> = if (targetDescriptor is PackageViewDescriptor) {
val psiFacade = JavaPsiFacade.getInstance(ref.element.project)
val fqName = targetDescriptor.fqName.asString()
listOfNotNull(psiFacade.findPackage(fqName))
} else {
DescriptorToSourceUtilsIde.getAllDeclarations(ref.element.project, targetDescriptor, ref.element.resolveScope)
}
private fun getLabelTargets(ref: KtFe10Reference, context: BindingContext): Collection<PsiElement>? {
val reference = ref.element as? KtReferenceExpression ?: return null
val labelTarget = context[BindingContext.LABEL_TARGET, reference]
if (labelTarget != null) {
return listOf(labelTarget)
}
return context[BindingContext.AMBIGUOUS_LABEL_TARGET, reference]
}
// TODO: Old impl from IDE side has this logic wrapped inside runWithCancellationCheck. Figure out what we should do here.
override fun resolve(ref: KtReference, incompleteCode: Boolean): Array<ResolveResult> {
require(ref is KtFe10Reference) { "reference should be KtFe10Reference, but was ${ref::class}" }
val resolveToPsiElements = resolveToPsiElements(ref)
return resolveToPsiElements.map { KotlinResolveResult(it) }.toTypedArray()
}
}
@@ -5,11 +5,57 @@
package org.jetbrains.kotlin.analysis.api.descriptors.references.base
import com.intellij.psi.PsiMethod
import com.intellij.psi.impl.source.resolve.ResolveCache
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
import org.jetbrains.kotlin.analysis.api.KtSymbolBasedReference
import org.jetbrains.kotlin.analysis.api.descriptors.KtFe10AnalysisSession
import org.jetbrains.kotlin.analysis.api.descriptors.symbols.descriptorBased.base.toKtSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KtSymbol
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.references.unwrappedTargets
import org.jetbrains.kotlin.psi.KtConstructor
import org.jetbrains.kotlin.psi.KtImportAlias
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.resolve.BindingContext
interface KtFe10Reference : KtReference, KtSymbolBasedReference {
override val resolver: ResolveCache.PolyVariantResolver<KtReference>
get() = KtFe10PolyVariantResolver
fun resolveToDescriptors(bindingContext: BindingContext): Collection<DeclarationDescriptor> = getTargetDescriptors(bindingContext)
fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor>
fun isReferenceToImportAlias(alias: KtImportAlias): Boolean {
val importDirective = alias.importDirective ?: return false
val importedFqName = importDirective.importedFqName ?: return false
val helper = KtFe10ReferenceResolutionHelper.getInstance()
val importedDescriptors = helper.resolveImportReference(importDirective.containingKtFile, importedFqName)
val importableTargets = unwrappedTargets.mapNotNull {
when {
it is KtConstructor<*> -> it.containingClassOrObject
it is PsiMethod && it.isConstructor -> it.containingClass
else -> it
}
}
val project = element.project
val resolveScope = element.resolveScope
return importedDescriptors.any {
helper.findPsiDeclarations(it, project, resolveScope).any { declaration ->
declaration in importableTargets
}
}
}
override fun KtAnalysisSession.resolveToSymbols(): Collection<KtSymbol> {
require(this is KtFe10AnalysisSession)
val bindingContext = KtFe10ReferenceResolutionHelper.getInstance().partialAnalyze(element)
return getTargetDescriptors(bindingContext).mapNotNull { descriptor ->
descriptor.toKtSymbol(analysisContext)
}
}
}
@@ -0,0 +1,40 @@
/*
* 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.api.descriptors.references.base
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
/**
* Temporary helper that allows FE1.0 KtReferences to use some IDE functionality.
*/
interface KtFe10ReferenceResolutionHelper {
fun isInProjectOrLibSource(element: PsiElement, includeScriptsOutsideSourceRoots: Boolean): Boolean
fun resolveImportReference(file: KtFile, fqName: FqName): Collection<DeclarationDescriptor>
fun partialAnalyze(element: KtElement): BindingContext
fun findDecompiledDeclaration(
project: Project,
referencedDescriptor: DeclarationDescriptor,
builtInsSearchScope: GlobalSearchScope?
): KtDeclaration?
fun findPsiDeclarations(declaration: DeclarationDescriptor, project: Project, resolveScope: GlobalSearchScope): Collection<PsiElement>
companion object {
fun getInstance() = ApplicationManager.getApplication().getService(KtFe10ReferenceResolutionHelper::class.java)
}
}
@@ -0,0 +1,48 @@
/*
* 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.api.descriptors.utils
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.descriptors.references.base.KtFe10ReferenceResolutionHelper
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.utils.addToStdlib.sequenceOfLazyValues
object DescriptorToSourceUtilsIde {
// Returns PSI element for descriptor. If there are many relevant elements (e.g. it is fake override
// with multiple declarations), finds any of them. It can find declarations in builtins or decompiled code.
fun getAnyDeclaration(project: Project, descriptor: DeclarationDescriptor): PsiElement? {
return getDeclarationsStream(project, descriptor).firstOrNull()
}
// Returns all PSI elements for descriptor. It can find declarations in builtins or decompiled code.
fun getAllDeclarations(
project: Project,
targetDescriptor: DeclarationDescriptor,
builtInsSearchScope: GlobalSearchScope? = null
): Collection<PsiElement> {
val result = getDeclarationsStream(project, targetDescriptor, builtInsSearchScope).toHashSet()
// filter out elements which are navigate to some other element of the result
// this is needed to avoid duplicated results for references to declaration in same library source file
return result.filter { element -> result.none { element != it && it.navigationElement == element } }
}
private fun getDeclarationsStream(
project: Project, targetDescriptor: DeclarationDescriptor, builtInsSearchScope: GlobalSearchScope? = null
): Sequence<PsiElement> {
val effectiveReferencedDescriptors = DescriptorToSourceUtils.getEffectiveReferencedDescriptors(targetDescriptor).asSequence()
return effectiveReferencedDescriptors.flatMap { effectiveReferenced ->
// References in library sources should be resolved to corresponding decompiled declarations,
// therefore we put both source declaration and decompiled declaration to stream, and afterwards we filter it in getAllDeclarations
sequenceOfLazyValues(
{ DescriptorToSourceUtils.getSourceFromDescriptor(effectiveReferenced) },
{ KtFe10ReferenceResolutionHelper.getInstance().findDecompiledDeclaration(project, effectiveReferenced, builtInsSearchScope) }
)
}.filterNotNull()
}
}
@@ -0,0 +1,60 @@
/*
* 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.idea.references
import com.intellij.openapi.application.ApplicationManager
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
import org.jetbrains.kotlin.resolve.references.ReferenceAccess
import org.jetbrains.kotlin.utils.addToStdlib.constant
interface ReadWriteAccessChecker {
fun readWriteAccessWithFullExpressionByResolve(assignment: KtBinaryExpression): Pair<ReferenceAccess, KtExpression>?
fun readWriteAccessWithFullExpression(
targetExpression: KtExpression,
useResolveForReadWrite: Boolean
): Pair<ReferenceAccess, KtExpression> {
var expression = targetExpression.getQualifiedExpressionForSelectorOrThis()
loop@ while (true) {
when (val parent = expression.parent) {
is KtParenthesizedExpression, is KtAnnotatedExpression, is KtLabeledExpression -> expression = parent as KtExpression
else -> break@loop
}
}
val assignment = expression.getAssignmentByLHS()
if (assignment != null) {
return when (assignment.operationToken) {
KtTokens.EQ -> ReferenceAccess.WRITE to assignment
else -> {
(if (useResolveForReadWrite) readWriteAccessWithFullExpressionByResolve(assignment) else null)
?: (ReferenceAccess.READ_WRITE to assignment)
}
}
}
val unaryExpression = expression.parent as? KtUnaryExpression
return if (unaryExpression != null && unaryExpression.operationToken in constant { setOf(KtTokens.PLUSPLUS, KtTokens.MINUSMINUS) })
ReferenceAccess.READ_WRITE to unaryExpression
else
ReferenceAccess.READ to expression
}
companion object {
fun getInstance(): ReadWriteAccessChecker = ApplicationManager.getApplication().getService(ReadWriteAccessChecker::class.java)
}
}
fun KtExpression.readWriteAccessWithFullExpression(useResolveForReadWrite: Boolean): Pair<ReferenceAccess, KtExpression> =
ReadWriteAccessChecker.getInstance().readWriteAccessWithFullExpression(this, useResolveForReadWrite)
fun KtExpression.readWriteAccess(useResolveForReadWrite: Boolean): ReferenceAccess =
ReadWriteAccessChecker.getInstance().readWriteAccessWithFullExpression(this, useResolveForReadWrite).first
@@ -0,0 +1,22 @@
/*
* 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.plugin.references
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.psi.KtPsiFactory
interface SimpleNameReferenceExtension {
companion object {
val EP_NAME: ExtensionPointName<SimpleNameReferenceExtension> =
ExtensionPointName.create("org.jetbrains.kotlin.simpleNameReferenceExtension")
}
fun isReferenceTo(reference: KtSimpleNameReference, element: PsiElement): Boolean
fun handleElementRename(reference: KtSimpleNameReference, psiFactory: KtPsiFactory, newElementName: String): PsiElement?
}