FIR IDE: Make KtReference class descriptors frontend independent

* The new idea-frontend-independent module created
* Moved KtReference and it inheritors to that module & implement them in idea-analysis module by using descriptors frontend
This commit is contained in:
Ilya Kirillov
2020-05-07 21:25:08 +03:00
parent 6adad1055b
commit 418903e9ef
52 changed files with 1045 additions and 730 deletions
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2016 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.
* Copyright 2010-2020 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.kdoc
@@ -20,6 +9,7 @@ import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.references.KtDescriptorsBasedReference
import org.jetbrains.kotlin.idea.references.KtMultiReference
import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
@@ -28,7 +18,11 @@ import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
class KDocReference(element: KDocName) : KtMultiReference<KDocName>(element) {
//TODO move to frontend-independent module
class KDocReference(element: KDocName) : KtMultiReference<KDocName>(element), KtDescriptorsBasedReference {
override fun isReferenceTo(element: PsiElement): Boolean =
super<KtDescriptorsBasedReference>.isReferenceTo(element)
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val declaration = element.getContainingDoc().getOwner() ?: return arrayListOf()
val resolutionFacade = element.getResolutionFacade()
@@ -0,0 +1,164 @@
/*
* Copyright 2010-2020 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.psi.PsiElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.types.ConeLookupTagBasedType
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.idea.fir.firResolveState
import org.jetbrains.kotlin.idea.fir.getOrBuildFir
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtPackageDirective
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
object FirReferenceResolveHelper {
fun FirResolvedTypeRef.toTargetPsi(session: FirSession): PsiElement? {
val type = type as? ConeLookupTagBasedType ?: return null
return (type.lookupTag.toSymbol(session) as? AbstractFirBasedSymbol<*>)?.fir?.psi
}
fun ClassId.toTargetPsi(session: FirSession, calleeReference: FirReference? = null): PsiElement? {
val classLikeDeclaration = ConeClassLikeLookupTagImpl(this).toSymbol(session)?.fir
if (classLikeDeclaration is FirRegularClass) {
if (calleeReference is FirResolvedNamedReference) {
val callee = calleeReference.resolvedSymbol.fir as? FirCallableMemberDeclaration
// TODO: check callee owner directly?
if (callee !is FirConstructor && callee?.isStatic != true) {
classLikeDeclaration.companionObject?.let { return it.psi }
}
}
}
return classLikeDeclaration?.psi
}
fun FirReference.toTargetPsi(session: FirSession): PsiElement? {
return when (this) {
is FirResolvedNamedReference -> {
resolvedSymbol.fir.psi
}
is FirResolvedCallableReference -> {
resolvedSymbol.fir.psi
}
is FirThisReference -> {
boundSymbol?.fir?.psi
}
is FirSuperReference -> {
(superTypeRef as? FirResolvedTypeRef)?.toTargetPsi(session)
}
else -> {
null
}
}
}
fun resolveToPsiElements(ref: AbstractKtReference<KtElement>): Collection<PsiElement> {
val expression = ref.expression
val state = expression.firResolveState()
val session = state.getSession(expression)
when (val fir = expression.getOrBuildFir(state)) {
is FirResolvable -> {
return listOfNotNull(fir.calleeReference.toTargetPsi(session))
}
is FirResolvedTypeRef -> {
return listOfNotNull(fir.toTargetPsi(session))
}
is FirResolvedQualifier -> {
val classId = fir.classId ?: return emptyList()
// Distinguish A.foo() from A(.Companion).foo()
// Make expression.parent as? KtDotQualifiedExpression local function
var parent = expression.parent as? KtDotQualifiedExpression
while (parent != null) {
val selectorExpression = parent.selectorExpression ?: break
if (selectorExpression === expression) {
parent = parent.parent as? KtDotQualifiedExpression
continue
}
val parentFir = selectorExpression.getOrBuildFir(state)
if (parentFir is FirQualifiedAccess) {
return listOfNotNull(classId.toTargetPsi(session, parentFir.calleeReference))
}
parent = parent.parent as? KtDotQualifiedExpression
}
return listOfNotNull(classId.toTargetPsi(session))
}
is FirAnnotationCall -> {
val type = fir.typeRef as? FirResolvedTypeRef ?: return emptyList()
return listOfNotNull(type.toTargetPsi(session))
}
is FirResolvedImport -> {
var parent = expression.parent
while (parent is KtDotQualifiedExpression) {
if (parent.selectorExpression !== expression) {
// Special: package reference in the middle of import directive
// import a.<caret>b.c.SomeClass
// TODO: return reference to PsiPackage
return listOf(expression)
}
parent = parent.parent
}
val classId = fir.resolvedClassId
if (classId != null) {
return listOfNotNull(classId.toTargetPsi(session))
}
val name = fir.importedName ?: return emptyList()
val symbolProvider = session.firSymbolProvider
return symbolProvider.getTopLevelCallableSymbols(fir.packageFqName, name).mapNotNull { it.fir.psi } +
listOfNotNull(symbolProvider.getClassLikeSymbolByFqName(ClassId(fir.packageFqName, name))?.fir?.psi)
}
is FirFile -> {
if (expression.getNonStrictParentOfType<KtPackageDirective>() != null) {
// Special: package reference in the middle of package directive
return listOf(expression)
}
return listOfNotNull(fir.psi)
}
is FirArrayOfCall -> {
// We can't yet find PsiElement for arrayOf, intArrayOf, etc.
return emptyList()
}
is FirErrorNamedReference -> {
return emptyList()
}
else -> {
// Handle situation when we're in the middle/beginning of qualifier
// <caret>A.B.C.foo() or A.<caret>B.C.foo()
// NB: in this case we get some parent FIR, like FirBlock, FirProperty, FirFunction or the like
var parent = expression.parent as? KtDotQualifiedExpression
var unresolvedCounter = 1
while (parent != null) {
val selectorExpression = parent.selectorExpression ?: break
if (selectorExpression === expression) {
parent = parent.parent as? KtDotQualifiedExpression
continue
}
val parentFir = selectorExpression.getOrBuildFir(state)
if (parentFir is FirResolvedQualifier) {
var classId = parentFir.classId
while (unresolvedCounter > 0) {
unresolvedCounter--
classId = classId?.outerClassId
}
return listOfNotNull(classId?.toTargetPsi(session))
}
parent = parent.parent as? KtDotQualifiedExpression
unresolvedCounter++
}
return emptyList()
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2020 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.
*/
@@ -66,14 +66,10 @@ class ReferenceImpl(private val argument: KtValueArgument) : PsiReference {
override fun isSoft() = false
}
internal class KotlinDefaultAnnotationMethodImplicitReferenceContributor : KotlinReferenceProviderContributor {
override fun registerReferenceProviders(registrar: KotlinPsiReferenceRegistrar) {
registrar.registerProvider<KtValueArgument> {
if (it.isNamed()) return@registerProvider null
val annotationEntry = it.getParentOfTypeAndBranch<KtAnnotationEntry> { valueArgumentList } ?: return@registerProvider null
if (annotationEntry.valueArguments.size != 1) return@registerProvider null
internal val KotlinDefaultAnnotationMethodImplicitReferenceProvider = provider@{ element: KtValueArgument ->
if (element.isNamed()) return@provider null
val annotationEntry = element.getParentOfTypeAndBranch<KtAnnotationEntry> { valueArgumentList } ?: return@provider null
if (annotationEntry.valueArguments.size != 1) return@provider null
ReferenceImpl(it)
}
}
}
ReferenceImpl(element)
}
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2020 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
@@ -24,10 +13,10 @@ import org.jetbrains.kotlin.psi.KtPackageDirective
import org.jetbrains.kotlin.psi.KtUserType
import org.jetbrains.kotlin.psi.psiUtil.parents
internal class KotlinReferenceContributor : KotlinReferenceProviderContributor {
class KotlinReferenceContributor : KotlinReferenceProviderContributor {
override fun registerReferenceProviders(registrar: KotlinPsiReferenceRegistrar) {
with(registrar) {
registerProvider(factory = ::KtSimpleNameReference)
registerProvider(factory = ::KtSimpleNameReferenceDescriptorsImpl)
registerMultiProvider<KtNameReferenceExpression> { nameReferenceExpression ->
if (nameReferenceExpression.getReferencedNameElementType() != KtTokens.IDENTIFIER) return@registerMultiProvider emptyArray()
@@ -37,32 +26,34 @@ internal class KotlinReferenceContributor : KotlinReferenceProviderContributor {
when (nameReferenceExpression.readWriteAccess(useResolveForReadWrite = false)) {
ReferenceAccess.READ ->
arrayOf(SyntheticPropertyAccessorReference.Getter(nameReferenceExpression))
arrayOf(SyntheticPropertyAccessorReferenceDescriptorImpl(nameReferenceExpression, getter = true))
ReferenceAccess.WRITE ->
arrayOf(SyntheticPropertyAccessorReference.Setter(nameReferenceExpression))
arrayOf(SyntheticPropertyAccessorReferenceDescriptorImpl(nameReferenceExpression, getter = false))
ReferenceAccess.READ_WRITE ->
arrayOf(
SyntheticPropertyAccessorReference.Getter(nameReferenceExpression),
SyntheticPropertyAccessorReference.Setter(nameReferenceExpression)
SyntheticPropertyAccessorReferenceDescriptorImpl(nameReferenceExpression, getter = true),
SyntheticPropertyAccessorReferenceDescriptorImpl(nameReferenceExpression, getter = false)
)
}
}
registerProvider(factory = ::KtConstructorDelegationReference)
registerProvider(factory = ::KtConstructorDelegationReferenceDescriptorsImpl)
registerProvider(factory = ::KtInvokeFunctionReference)
registerProvider(factory = ::KtInvokeFunctionReferenceDescriptorsImpl)
registerProvider(factory = ::KtArrayAccessReference)
registerProvider(factory = ::KtArrayAccessReferenceDescriptorsImpl)
registerProvider(factory = ::KtCollectionLiteralReference)
registerProvider(factory = ::KtCollectionLiteralReferenceDescriptorsImpl)
registerProvider(factory = ::KtForLoopInReference)
registerProvider(factory = ::KtForLoopInReferenceDescriptorsImpl)
registerProvider(factory = ::KtPropertyDelegationMethodsReference)
registerProvider(factory = ::KtPropertyDelegationMethodsReferenceDescriptorsImpl)
registerProvider(factory = ::KtDestructuringDeclarationReference)
registerProvider(factory = ::KtDestructuringDeclarationReferenceDescriptorsImpl)
registerProvider(factory = ::KDocReference)
registerProvider(KotlinDefaultAnnotationMethodImplicitReferenceProvider)
}
}
}
@@ -1,80 +0,0 @@
/*
* Copyright 2010-2019 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.google.common.collect.Lists
import com.intellij.psi.MultiRangeReference
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.core.canMoveLambdaOutsideParentheses
import org.jetbrains.kotlin.idea.core.moveFunctionLiteralOutsideParentheses
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.lexer.KtToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.psi.buildExpression
import org.jetbrains.kotlin.psi.psiUtil.getPossiblyQualifiedCallExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContext.INDEXED_LVALUE_GET
import org.jetbrains.kotlin.resolve.BindingContext.INDEXED_LVALUE_SET
import org.jetbrains.kotlin.util.OperatorNameConventions
class KtArrayAccessReference(
expression: KtArrayAccessExpression
) : KtSimpleReference<KtArrayAccessExpression>(expression), MultiRangeReference {
override val resolvesByNames: Collection<Name>
get() = NAMES
override fun getRangeInElement() = element.textRange.shiftRight(-element.textOffset)
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val getFunctionDescriptor = context[INDEXED_LVALUE_GET, expression]?.candidateDescriptor
val setFunctionDescriptor = context[INDEXED_LVALUE_SET, expression]?.candidateDescriptor
return listOfNotNull(getFunctionDescriptor, setFunctionDescriptor)
}
private fun getBracketRange(bracketToken: KtToken) =
expression.indicesNode.node.findChildByType(bracketToken)?.textRange?.shiftRight(-expression.textOffset)
override fun getRanges() = listOfNotNull(getBracketRange(KtTokens.LBRACKET), getBracketRange(KtTokens.RBRACKET))
override fun canRename() = true
override fun handleElementRename(newElementName: String): PsiElement? {
val arrayAccessExpression = expression
if (OperatorNameConventions.INVOKE.asString() == newElementName) {
val replacement = KtPsiFactory(arrayAccessExpression.project).buildExpression {
val arrayExpression = arrayAccessExpression.arrayExpression
if (arrayExpression is KtQualifiedExpression) {
appendExpression(arrayExpression.receiverExpression)
appendFixedText(arrayExpression.operationSign.value)
appendExpression(arrayExpression.selectorExpression)
} else {
appendExpression(arrayExpression)
}
appendFixedText("(")
appendExpressions(arrayAccessExpression.indexExpressions, ",")
appendFixedText(")")
}
val fullCallExpression = arrayAccessExpression.replaced(replacement)
val callExpression = fullCallExpression.getPossiblyQualifiedCallExpression()
if (callExpression != null && callExpression.canMoveLambdaOutsideParentheses()) {
callExpression.moveFunctionLiteralOutsideParentheses()
}
return fullCallExpression
}
return this.renameImplicitConventionalCall(newElementName)
}
companion object {
private val NAMES = Lists.newArrayList(OperatorNameConventions.GET, OperatorNameConventions.SET)
}
}
@@ -0,0 +1,41 @@
/*
* Copyright 2010-2020 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.psi.MultiRangeReference
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.core.canMoveLambdaOutsideParentheses
import org.jetbrains.kotlin.idea.core.moveFunctionLiteralOutsideParentheses
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContext.INDEXED_LVALUE_GET
import org.jetbrains.kotlin.resolve.BindingContext.INDEXED_LVALUE_SET
internal class KtArrayAccessReferenceDescriptorsImpl(
expression: KtArrayAccessExpression
) : KtArrayAccessReference(expression), KtDescriptorsBasedReference {
override fun isReferenceTo(element: PsiElement): Boolean =
super<KtDescriptorsBasedReference>.isReferenceTo(element)
override fun moveFunctionLiteralOutsideParentheses(callExpression: KtCallExpression) {
callExpression.moveFunctionLiteralOutsideParentheses()
}
override fun canMoveLambdaOutsideParentheses(callExpression: KtCallExpression): Boolean =
callExpression.canMoveLambdaOutsideParentheses()
override fun doRenameImplicitConventionalCall(newName: String?): KtExpression =
renameImplicitConventionalCall(newName)
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val getFunctionDescriptor = context[INDEXED_LVALUE_GET, expression]?.candidateDescriptor
val setFunctionDescriptor = context[INDEXED_LVALUE_SET, expression]?.candidateDescriptor
return listOfNotNull(getFunctionDescriptor, setFunctionDescriptor)
}
}
@@ -1,39 +0,0 @@
/*
* Copyright 2010-2019 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.util.TextRange
import com.intellij.psi.MultiRangeReference
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.CollectionLiteralResolver
class KtCollectionLiteralReference(expression: KtCollectionLiteralExpression) :
KtSimpleReference<KtCollectionLiteralExpression>(expression), MultiRangeReference {
companion object {
private val COLLECTION_LITERAL_CALL_NAMES =
CollectionLiteralResolver.PRIMITIVE_TYPE_TO_ARRAY.values + CollectionLiteralResolver.ARRAY_OF_FUNCTION
}
override fun getRangeInElement(): TextRange = element.normalizeRange()
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val resolvedCall = context[BindingContext.COLLECTION_LITERAL_CALL, element]
return listOfNotNull(resolvedCall?.resultingDescriptor)
}
override fun getRanges(): List<TextRange> {
return listOfNotNull(element.leftBracket?.normalizeRange(), element.rightBracket?.normalizeRange())
}
override val resolvesByNames: Collection<Name>
get() = COLLECTION_LITERAL_CALL_NAMES
private fun PsiElement.normalizeRange(): TextRange = this.textRange.shiftRight(-expression.textOffset)
}
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2020 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.psi.PsiElement
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression
import org.jetbrains.kotlin.resolve.BindingContext
class KtCollectionLiteralReferenceDescriptorsImpl(
expression: KtCollectionLiteralExpression
) : KtCollectionLiteralReference(expression), KtDescriptorsBasedReference {
override fun isReferenceTo(element: PsiElement): Boolean =
super<KtDescriptorsBasedReference>.isReferenceTo(element)
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val resolvedCall = context[BindingContext.COLLECTION_LITERAL_CALL, element]
return listOfNotNull(resolvedCall?.resultingDescriptor)
}
}
@@ -1,51 +0,0 @@
/*
* Copyright 2010-2015 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.references;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.KtConstructorDelegationReferenceExpression;
import java.util.Collection;
import java.util.Collections;
public class KtConstructorDelegationReference extends KtSimpleReference<KtConstructorDelegationReferenceExpression> {
public KtConstructorDelegationReference(KtConstructorDelegationReferenceExpression expression) {
super(expression);
}
@Override
public TextRange getRangeInElement() {
return new TextRange(0, getElement().getTextLength());
}
@NotNull
@Override
public Collection<Name> getResolvesByNames() {
return Collections.emptyList();
}
@Nullable
@Override
public PsiElement handleElementRename(@Nullable String newElementName) {
// Class rename never affects this reference, so there is no need to fail with exception
return getExpression();
}
}
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2020 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.psi.PsiElement
import org.jetbrains.kotlin.psi.KtConstructorDelegationReferenceExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
class KtConstructorDelegationReferenceDescriptorsImpl(
expression: KtConstructorDelegationReferenceExpression
) : KtConstructorDelegationReference(expression), KtDescriptorsBasedReference {
override fun isReferenceTo(element: PsiElement): Boolean =
super<KtDescriptorsBasedReference>.isReferenceTo(element)
override fun getTargetDescriptors(context: BindingContext) = expression.getReferenceTargets(context)
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2020 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.
*/
@@ -16,8 +16,12 @@ import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry
import org.jetbrains.kotlin.resolve.BindingContext
class KtDestructuringDeclarationReference(element: KtDestructuringDeclarationEntry) :
AbstractKtReference<KtDestructuringDeclarationEntry>(element) {
class KtDestructuringDeclarationReferenceDescriptorsImpl(
element: KtDestructuringDeclarationEntry
) : KtDestructuringDeclarationReference(element), KtDescriptorsBasedReference {
override fun isReferenceTo(element: PsiElement): Boolean =
super<KtDescriptorsBasedReference>.isReferenceTo(element)
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
return listOfNotNull(context[BindingContext.COMPONENT_RESOLVED_CALL, element]?.candidateDescriptor)
}
@@ -30,16 +34,4 @@ class KtDestructuringDeclarationReference(element: KtDestructuringDeclarationEnt
it is CallableMemberDescriptor && it.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
}
}
override fun handleElementRename(newElementName: String): PsiElement? {
if (canRename()) return expression
throw IncorrectOperationException()
}
override val resolvesByNames: Collection<Name>
get() {
val destructuringParent = element.parent as? KtDestructuringDeclaration ?: return emptyList()
val componentIndex = destructuringParent.entries.indexOf(element) + 1
return listOf(Name.identifier("component$componentIndex"))
}
}
@@ -1,45 +1,34 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2020 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.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtForExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.util.OperatorNameConventions
class KtForLoopInReference(element: KtForExpression) : KtMultiReference<KtForExpression>(element) {
override fun getRangeInElement(): TextRange {
val inKeyword = expression.inKeyword ?: return TextRange.EMPTY_RANGE
val offset = inKeyword.startOffsetInParent
return TextRange(offset, offset + inKeyword.textLength)
}
class KtForLoopInReferenceDescriptorsImpl(
element: KtForExpression
) : KtForLoopInReference(element), KtDescriptorsBasedReference {
override fun isReferenceTo(element: PsiElement): Boolean =
super<KtDescriptorsBasedReference>.isReferenceTo(element)
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val loopRange = expression.loopRange ?: return emptyList()
return LOOP_RANGE_KEYS.mapNotNull { key -> context.get(key, loopRange)?.candidateDescriptor }
}
override val resolvesByNames: Collection<Name>
get() = NAMES
companion object {
private val LOOP_RANGE_KEYS = arrayOf(
BindingContext.LOOP_RANGE_ITERATOR_RESOLVED_CALL,
BindingContext.LOOP_RANGE_NEXT_RESOLVED_CALL,
BindingContext.LOOP_RANGE_HAS_NEXT_RESOLVED_CALL
)
private val NAMES = listOf(
OperatorNameConventions.ITERATOR,
OperatorNameConventions.NEXT,
OperatorNameConventions.HAS_NEXT
)
}
}
@@ -1,111 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.project.IndexNotReadyException
import com.intellij.psi.ContributedReferenceHost
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiReferenceService
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.util.containers.ConcurrentFactoryMap
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.psi.KotlinReferenceProvidersService
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.utils.SmartList
internal interface KotlinPsiReferenceProvider {
fun getReferencesByElement(element: PsiElement): Array<PsiReference>
}
internal interface KotlinReferenceProviderContributor {
fun registerReferenceProviders(registrar: KotlinPsiReferenceRegistrar)
}
internal class KotlinPsiReferenceRegistrar {
val providers: MultiMap<Class<out PsiElement>, KotlinPsiReferenceProvider> = MultiMap.create()
inline fun <reified E : KtElement> registerProvider(crossinline factory: (E) -> PsiReference?) {
registerMultiProvider<E> { element ->
factory(element)?.let { reference -> arrayOf(reference) } ?: PsiReference.EMPTY_ARRAY
}
}
inline fun <reified E : KtElement> registerMultiProvider(crossinline factory: (E) -> Array<PsiReference>) {
val provider: KotlinPsiReferenceProvider = object : KotlinPsiReferenceProvider {
override fun getReferencesByElement(element: PsiElement): Array<PsiReference> {
@Suppress("UNCHECKED_CAST")
return factory(element as E)
}
}
registerMultiProvider(E::class.java, provider)
}
fun registerMultiProvider(klass: Class<out PsiElement>, provider: KotlinPsiReferenceProvider) {
providers.putValue(klass, provider)
}
}
class KtIdeReferenceProviderService : KotlinReferenceProvidersService() {
private val originalProvidersBinding: MultiMap<Class<out PsiElement>, KotlinPsiReferenceProvider>
private val providersBindingCache: Map<Class<out PsiElement>, List<KotlinPsiReferenceProvider>>
private val referenceContributors = listOf(KotlinReferenceContributor(), KotlinDefaultAnnotationMethodImplicitReferenceContributor())
init {
val registrar = KotlinPsiReferenceRegistrar()
referenceContributors.forEach { it.registerReferenceProviders(registrar) }
originalProvidersBinding = registrar.providers
providersBindingCache = ConcurrentFactoryMap.createMap<Class<out PsiElement>, List<KotlinPsiReferenceProvider>> { klass ->
val result = ContainerUtil.newSmartList<KotlinPsiReferenceProvider>()
for (bindingClass in originalProvidersBinding.keySet()) {
if (bindingClass.isAssignableFrom(klass)) {
result.addAll(originalProvidersBinding.get(bindingClass))
}
}
result
}
}
private fun doGetKotlinReferencesFromProviders(context: PsiElement): Array<PsiReference> {
val providers: List<KotlinPsiReferenceProvider>? = providersBindingCache[context.javaClass]
if (providers.isNullOrEmpty()) return PsiReference.EMPTY_ARRAY
val result = SmartList<PsiReference>()
for (provider in providers) {
try {
result.addAll(provider.getReferencesByElement(context))
} catch (ignored: IndexNotReadyException) {
// Ignore and continue to next provider
}
}
if (result.isEmpty()) {
return PsiReference.EMPTY_ARRAY
}
return result.toTypedArray()
}
override fun getReferences(psiElement: PsiElement): Array<PsiReference> {
if (psiElement is ContributedReferenceHost) {
return ReferenceProvidersRegistry.getReferencesFromProviders(psiElement, PsiReferenceService.Hints.NO_HINTS)
}
return CachedValuesManager.getCachedValue(psiElement) {
CachedValueProvider.Result.create(
doGetKotlinReferencesFromProviders(psiElement),
PsiModificationTracker.MODIFICATION_COUNT
)
}
}
}
@@ -1,121 +0,0 @@
/*
* Copyright 2010-2015 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.references
import com.intellij.lang.ASTNode
import com.intellij.openapi.util.TextRange
import com.intellij.psi.MultiRangeReference
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.util.OperatorNameConventions
import java.util.*
class KtInvokeFunctionReference(expression: KtCallExpression) : KtSimpleReference<KtCallExpression>(expression), MultiRangeReference {
override val resolvesByNames: Collection<Name>
get() = NAMES
override fun getRangeInElement(): TextRange {
return element.textRange.shiftRight(-element.textOffset)
}
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val call = element.getCall(context)
val resolvedCall = call.getResolvedCall(context)
return when {
resolvedCall is VariableAsFunctionResolvedCall ->
setOf<DeclarationDescriptor>((resolvedCall as VariableAsFunctionResolvedCall).functionCall.candidateDescriptor)
call != null && resolvedCall != null && call.callType == Call.CallType.INVOKE ->
setOf<DeclarationDescriptor>(resolvedCall.candidateDescriptor)
else ->
emptyList()
}
}
override fun getRanges(): List<TextRange> {
val list = ArrayList<TextRange>()
val valueArgumentList = expression.valueArgumentList
if (valueArgumentList != null) {
if (valueArgumentList.arguments.isNotEmpty()) {
val valueArgumentListNode = valueArgumentList.node
val lPar = valueArgumentListNode.findChildByType(KtTokens.LPAR)
if (lPar != null) {
list.add(getRange(lPar))
}
val rPar = valueArgumentListNode.findChildByType(KtTokens.RPAR)
if (rPar != null) {
list.add(getRange(rPar))
}
} else {
list.add(getRange(valueArgumentList.node))
}
}
val functionLiteralArguments = expression.lambdaArguments
for (functionLiteralArgument in functionLiteralArguments) {
val functionLiteralExpression = functionLiteralArgument.getLambdaExpression() ?: continue
list.add(getRange(functionLiteralExpression.leftCurlyBrace))
val rightCurlyBrace = functionLiteralExpression.rightCurlyBrace
if (rightCurlyBrace != null) {
list.add(getRange(rightCurlyBrace))
}
}
return list
}
private fun getRange(node: ASTNode): TextRange {
val textRange = node.textRange
return textRange.shiftRight(-expression.textOffset)
}
override fun canRename(): Boolean = true
override fun handleElementRename(newElementName: String): PsiElement? {
val callExpression = expression
val fullCallExpression = callExpression.getQualifiedExpressionForSelectorOrThis()
if (newElementName == OperatorNameConventions.GET.asString() && callExpression.typeArguments.isEmpty()) {
val arrayAccessExpression = KtPsiFactory(callExpression).buildExpression {
if (fullCallExpression is KtQualifiedExpression) {
appendExpression(fullCallExpression.receiverExpression)
appendFixedText(fullCallExpression.operationSign.value)
}
appendExpression(callExpression.calleeExpression)
appendFixedText("[")
appendExpressions(callExpression.valueArguments.map { it.getArgumentExpression() })
appendFixedText("]")
}
return fullCallExpression.replace(arrayAccessExpression)
}
return renameImplicitConventionalCall(newElementName)
}
companion object {
private val NAMES = listOf(OperatorNameConventions.INVOKE)
}
}
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2020 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.psi.PsiElement
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
class KtInvokeFunctionReferenceDescriptorsImpl(expression: KtCallExpression) : KtInvokeFunctionReference(expression), KtDescriptorsBasedReference {
override fun isReferenceTo(element: PsiElement): Boolean =
super<KtDescriptorsBasedReference>.isReferenceTo(element)
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val call = element.getCall(context)
val resolvedCall = call.getResolvedCall(context)
return when {
resolvedCall is VariableAsFunctionResolvedCall ->
setOf<DeclarationDescriptor>((resolvedCall as VariableAsFunctionResolvedCall).functionCall.candidateDescriptor)
call != null && resolvedCall != null && call.callType == Call.CallType.INVOKE ->
setOf<DeclarationDescriptor>(resolvedCall.candidateDescriptor)
else ->
emptyList()
}
}
override fun doRenameImplicitConventionalCall(newName: String?): KtExpression =
renameImplicitConventionalCall(newName)
}
@@ -1,56 +0,0 @@
/*
* Copyright 2010-2015 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.references
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
import org.jetbrains.kotlin.descriptors.accessors
import org.jetbrains.kotlin.name.Name
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
import org.jetbrains.kotlin.util.OperatorNameConventions
class KtPropertyDelegationMethodsReference(element: KtPropertyDelegate) : KtMultiReference<KtPropertyDelegate>(element) {
override fun getRangeInElement(): TextRange {
val byKeywordNode = expression.byKeywordNode
val offset = byKeywordNode.psi!!.startOffsetInParent
return TextRange(offset, offset + byKeywordNode.textLength)
}
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))
}
override val resolvesByNames: Collection<Name> get() = NAMES
companion object {
private val NAMES = listOf(
OperatorNameConventions.GET_VALUE,
OperatorNameConventions.SET_VALUE,
OperatorNameConventions.PROVIDE_DELEGATE
)
}
}
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2020 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.psi.PsiElement
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
import org.jetbrains.kotlin.descriptors.accessors
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 KtPropertyDelegationMethodsReferenceDescriptorsImpl(
element: KtPropertyDelegate
) : KtPropertyDelegationMethodsReference(element), KtDescriptorsBasedReference {
override fun isReferenceTo(element: PsiElement): Boolean =
super<KtDescriptorsBasedReference>.isReferenceTo(element)
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)
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2015 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.
* Copyright 2010-2020 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
@@ -51,251 +40,83 @@ import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import java.util.*
interface KtReference : PsiPolyVariantReference {
fun resolveToDescriptors(bindingContext: BindingContext): Collection<DeclarationDescriptor>
interface KtDescriptorsBasedReference : KtReference {
override val resolver get() = KotlinDescriptorsBasedReferenceResolver
override fun getElement(): KtElement
val resolvesByNames: Collection<Name>
}
abstract class AbstractKtReference<T : KtElement>(element: T) : PsiPolyVariantReferenceBase<T>(element), KtReference {
val expression: T
get() = element
override fun multiResolve(incompleteCode: Boolean): Array<ResolveResult> {
@Suppress("UNCHECKED_CAST")
val kotlinResolver = KOTLIN_RESOLVER as ResolveCache.PolyVariantResolver<AbstractKtReference<T>>
return ResolveCache.getInstance(expression.project).resolveWithCaching(this, kotlinResolver, false, incompleteCode)
fun resolveToDescriptors(bindingContext: BindingContext): Collection<DeclarationDescriptor> {
return getTargetDescriptors(bindingContext)
}
fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor>
override fun isReferenceTo(element: PsiElement): Boolean {
return matchesTarget(element)
}
override fun getCanonicalText(): String = "<TBD>"
open fun canRename(): Boolean = false
override fun handleElementRename(newElementName: String): PsiElement? = throw IncorrectOperationException()
override fun bindToElement(element: PsiElement): PsiElement = throw IncorrectOperationException()
@Suppress("UNCHECKED_CAST")
override fun getVariants(): Array<Any> = PsiReference.EMPTY_ARRAY as Array<Any>
override fun isSoft(): Boolean = false
override fun resolveToDescriptors(bindingContext: BindingContext): Collection<DeclarationDescriptor> {
return getTargetDescriptors(bindingContext)
}
protected abstract fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor>
override fun toString() = this::class.java.simpleName + ": " + expression.text
companion object {
private object FirReferenceResolveHelper {
fun FirResolvedTypeRef.toTargetPsi(session: FirSession): PsiElement? {
val type = type as? ConeLookupTagBasedType ?: return null
return (type.lookupTag.toSymbol(session) as? AbstractFirBasedSymbol<*>)?.fir?.psi
}
fun ClassId.toTargetPsi(session: FirSession, calleeReference: FirReference? = null): PsiElement? {
val classLikeDeclaration = ConeClassLikeLookupTagImpl(this).toSymbol(session)?.fir
if (classLikeDeclaration is FirRegularClass) {
if (calleeReference is FirResolvedNamedReference) {
val callee = calleeReference.resolvedSymbol.fir as? FirCallableMemberDeclaration
// TODO: check callee owner directly?
if (callee !is FirConstructor && callee?.isStatic != true) {
classLikeDeclaration.companionObject?.let { return it.psi }
}
}
}
return classLikeDeclaration?.psi
}
fun FirReference.toTargetPsi(session: FirSession): PsiElement? {
return when (this) {
is FirResolvedNamedReference -> {
resolvedSymbol.fir.psi
}
is FirResolvedCallableReference -> {
resolvedSymbol.fir.psi
}
is FirThisReference -> {
boundSymbol?.fir?.psi
}
is FirSuperReference -> {
(superTypeRef as? FirResolvedTypeRef)?.toTargetPsi(session)
}
else -> {
null
}
}
}
fun resolveToPsiElements(ref: AbstractKtReference<KtElement>): Collection<PsiElement> {
val expression = ref.expression
val state = expression.firResolveState()
val session = state.getSession(expression)
when (val fir = expression.getOrBuildFir(state)) {
is FirResolvable -> {
return listOfNotNull(fir.calleeReference.toTargetPsi(session))
}
is FirResolvedTypeRef -> {
return listOfNotNull(fir.toTargetPsi(session))
}
is FirResolvedQualifier -> {
val classId = fir.classId ?: return emptyList()
// Distinguish A.foo() from A(.Companion).foo()
// Make expression.parent as? KtDotQualifiedExpression local function
var parent = expression.parent as? KtDotQualifiedExpression
while (parent != null) {
val selectorExpression = parent.selectorExpression ?: break
if (selectorExpression === expression) {
parent = parent.parent as? KtDotQualifiedExpression
continue
}
val parentFir = selectorExpression.getOrBuildFir(state)
if (parentFir is FirQualifiedAccess) {
return listOfNotNull(classId.toTargetPsi(session, parentFir.calleeReference))
}
parent = parent.parent as? KtDotQualifiedExpression
}
return listOfNotNull(classId.toTargetPsi(session))
}
is FirAnnotationCall -> {
val type = fir.typeRef as? FirResolvedTypeRef ?: return emptyList()
return listOfNotNull(type.toTargetPsi(session))
}
is FirResolvedImport -> {
var parent = expression.parent
while (parent is KtDotQualifiedExpression) {
if (parent.selectorExpression !== expression) {
// Special: package reference in the middle of import directive
// import a.<caret>b.c.SomeClass
// TODO: return reference to PsiPackage
return listOf(expression)
}
parent = parent.parent
}
val classId = fir.resolvedClassId
if (classId != null) {
return listOfNotNull(classId.toTargetPsi(session))
}
val name = fir.importedName ?: return emptyList()
val symbolProvider = session.firSymbolProvider
return symbolProvider.getTopLevelCallableSymbols(fir.packageFqName, name).mapNotNull { it.fir.psi } +
listOfNotNull(symbolProvider.getClassLikeSymbolByFqName(ClassId(fir.packageFqName, name))?.fir?.psi)
}
is FirFile -> {
if (expression.getNonStrictParentOfType<KtPackageDirective>() != null) {
// Special: package reference in the middle of package directive
return listOf(expression)
}
return listOfNotNull(fir.psi)
}
is FirArrayOfCall -> {
// We can't yet find PsiElement for arrayOf, intArrayOf, etc.
return emptyList()
}
is FirErrorNamedReference -> {
return emptyList()
}
else -> {
// Handle situation when we're in the middle/beginning of qualifier
// <caret>A.B.C.foo() or A.<caret>B.C.foo()
// NB: in this case we get some parent FIR, like FirBlock, FirProperty, FirFunction or the like
var parent = expression.parent as? KtDotQualifiedExpression
var unresolvedCounter = 1
while (parent != null) {
val selectorExpression = parent.selectorExpression ?: break
if (selectorExpression === expression) {
parent = parent.parent as? KtDotQualifiedExpression
continue
}
val parentFir = selectorExpression.getOrBuildFir(state)
if (parentFir is FirResolvedQualifier) {
var classId = parentFir.classId
while (unresolvedCounter > 0) {
unresolvedCounter--
classId = classId?.outerClassId
}
return listOfNotNull(classId?.toTargetPsi(session))
}
parent = parent.parent as? KtDotQualifiedExpression
unresolvedCounter++
}
return emptyList()
}
}
}
}
class KotlinReferenceResolver : ResolveCache.PolyVariantResolver<AbstractKtReference<KtElement>> {
class KotlinResolveResult(element: PsiElement) : PsiElementResolveResult(element)
private fun resolveToPsiElements(ref: AbstractKtReference<KtElement>): Collection<PsiElement> {
if (FirResolution.enabled) {
return FirReferenceResolveHelper.resolveToPsiElements(ref)
}
val bindingContext = ref.expression.analyze(BodyResolveMode.PARTIAL)
return resolveToPsiElements(ref, bindingContext, ref.getTargetDescriptors(bindingContext))
}
private fun resolveToPsiElements(
ref: AbstractKtReference<KtElement>,
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: AbstractKtReference<KtElement>,
targetDescriptor: DeclarationDescriptor
): Collection<PsiElement> {
return if (targetDescriptor is PackageViewDescriptor) {
val psiFacade = JavaPsiFacade.getInstance(ref.expression.project)
val fqName = targetDescriptor.fqName.asString()
listOfNotNull(psiFacade.findPackage(fqName))
} else {
DescriptorToSourceUtilsIde.getAllDeclarations(ref.expression.project, targetDescriptor, ref.expression.resolveScope)
}
}
private fun getLabelTargets(ref: AbstractKtReference<KtElement>, context: BindingContext): Collection<PsiElement>? {
val reference = ref.expression as? KtReferenceExpression ?: return null
val labelTarget = context[BindingContext.LABEL_TARGET, reference]
if (labelTarget != null) {
return listOf(labelTarget)
}
return context[BindingContext.AMBIGUOUS_LABEL_TARGET, reference]
}
override fun resolve(ref: AbstractKtReference<KtElement>, incompleteCode: Boolean): Array<ResolveResult> {
return runWithCancellationCheck {
val resolveToPsiElements = resolveToPsiElements(ref)
resolveToPsiElements.map { KotlinResolveResult(it) }.toTypedArray()
}
}
}
val KOTLIN_RESOLVER = KotlinReferenceResolver()
}
}
abstract class KtSimpleReference<T : KtReferenceExpression>(expression: T) : AbstractKtReference<T>(expression) {
override fun getTargetDescriptors(context: BindingContext) = expression.getReferenceTargets(context)
fun KtReference.resolveToDescriptors(bindingContext: BindingContext): Collection<DeclarationDescriptor> {
if (this !is KtDescriptorsBasedReference) {
error("Reference $this should be KtDescriptorsBasedReference but was ${this::class}")
}
return resolveToDescriptors(bindingContext)
}
abstract class KtMultiReference<T : KtElement>(expression: T) : AbstractKtReference<T>(expression)
object KotlinDescriptorsBasedReferenceResolver : ResolveCache.PolyVariantResolver<KtReference> {
class KotlinResolveResult(element: PsiElement) : PsiElementResolveResult(element)
private fun resolveToPsiElements(ref: KtDescriptorsBasedReference): Collection<PsiElement> {
if (FirResolution.enabled) {
@Suppress("UNCHECKED_CAST")
return FirReferenceResolveHelper.resolveToPsiElements(ref as AbstractKtReference<KtElement>)
}
val bindingContext = ref.element.analyze(BodyResolveMode.PARTIAL)
return resolveToPsiElements(ref, bindingContext, ref.getTargetDescriptors(bindingContext))
}
private fun resolveToPsiElements(
ref: KtDescriptorsBasedReference,
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: KtDescriptorsBasedReference,
targetDescriptor: DeclarationDescriptor
): Collection<PsiElement> {
return 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: KtDescriptorsBasedReference, 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]
}
override fun resolve(ref: KtReference, incompleteCode: Boolean): Array<ResolveResult> {
return runWithCancellationCheck {
val resolveToPsiElements = resolveToPsiElements(ref as KtDescriptorsBasedReference)
resolveToPsiElements.map { KotlinResolveResult(it) }.toTypedArray()
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2020 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.
*/
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.idea.references
import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.IncorrectOperationException
import com.intellij.util.SmartList
@@ -33,15 +34,21 @@ import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor
import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.expressions.OperatorConventions
class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleReference<KtSimpleNameExpression>(expression) {
class KtSimpleNameReferenceDescriptorsImpl(
expression: KtSimpleNameExpression
) : KtSimpleNameReference(expression), KtDescriptorsBasedReference {
override fun doCanBeReferenceTo(candidateTarget: PsiElement): Boolean =
canBeReferenceTo(candidateTarget)
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
return SmartList<DeclarationDescriptor>().apply {
// Replace Java property with its accessor(s)
for (descriptor in super.getTargetDescriptors(context)) {
for (descriptor in expression.getReferenceTargets(context)) {
val sizeBefore = size
if (descriptor !is JavaPropertyDescriptor) {
@@ -71,7 +78,7 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
if (extension.isReferenceTo(this, element)) return true
}
return super.isReferenceTo(element)
return super<KtDescriptorsBasedReference>.isReferenceTo(element)
}
override fun getRangeInElement(): TextRange {
@@ -141,23 +148,18 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
return expression
}
enum class ShorteningMode {
NO_SHORTENING,
DELAYED_SHORTENING,
FORCED_SHORTENING
}
// By default reference binding is delayed
override fun bindToElement(element: PsiElement): PsiElement =
bindToElement(element, ShorteningMode.DELAYED_SHORTENING)
fun bindToElement(element: PsiElement, shorteningMode: ShorteningMode): PsiElement =
override fun bindToElement(element: PsiElement, shorteningMode: ShorteningMode): PsiElement =
element.getKotlinFqName()?.let { fqName -> bindToFqName(fqName, shorteningMode, element) } ?: expression
fun bindToFqName(
override fun bindToFqName(
fqName: FqName,
shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING,
targetElement: PsiElement? = null
shorteningMode: ShorteningMode,
targetElement: PsiElement?
): PsiElement {
val expression = expression
if (fqName.isRoot) return expression
@@ -289,7 +291,7 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
return listOf(element.getReferencedNameAsName())
}
fun getImportAlias(): KtImportAlias? {
override fun getImportAlias(): KtImportAlias? {
fun DeclarationDescriptor.unwrap() = if (this is ImportedFromObjectCallableDescriptor<*>) callableFromObject else this
val element = element
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2020 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 org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
import org.jetbrains.kotlin.types.expressions.OperatorConventions
class ReadWriteAccessCheckerDescriptorsImpl : ReadWriteAccessChecker {
override fun readWriteAccessWithFullExpressionByResolve(assignment: KtBinaryExpression): Pair<ReferenceAccess, KtExpression> {
val resolvedCall = assignment.resolveToCall() ?: return ReferenceAccess.READ_WRITE to assignment
if (!resolvedCall.isReallySuccess()) return ReferenceAccess.READ_WRITE to assignment
return if (resolvedCall.resultingDescriptor.name in OperatorConventions.ASSIGNMENT_OPERATIONS.values)
ReferenceAccess.READ to assignment
else
ReferenceAccess.READ_WRITE to assignment
}
}
@@ -1,13 +1,11 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2020 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.util.TextRange
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
@@ -22,14 +20,22 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.utils.addIfNotNull
sealed class SyntheticPropertyAccessorReference(expression: KtNameReferenceExpression, private val getter: Boolean) :
KtSimpleReference<KtNameReferenceExpression>(expression) {
class SyntheticPropertyAccessorReferenceDescriptorImpl(
expression: KtNameReferenceExpression,
getter: Boolean
) : SyntheticPropertyAccessorReference(expression, getter), KtDescriptorsBasedReference {
override fun isReferenceTo(element: PsiElement): Boolean =
super<SyntheticPropertyAccessorReference>.isReferenceTo(element)
override fun additionalIsReferenceToChecker(element: PsiElement): Boolean = matchesTarget(element)
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
val descriptors = super.getTargetDescriptors(context)
val descriptors = expression.getReferenceTargets(context)
if (descriptors.none { it is SyntheticJavaPropertyDescriptor }) return emptyList()
val result = SmartList<FunctionDescriptor>()
@@ -45,23 +51,6 @@ sealed class SyntheticPropertyAccessorReference(expression: KtNameReferenceExpre
return result
}
override fun isReferenceTo(element: PsiElement): Boolean {
if (element !is PsiMethod || !isAccessorName(element.name)) return false
if (!getter && expression.readWriteAccess(true) == ReferenceAccess.READ) return false
return super.isReferenceTo(element)
}
private fun isAccessorName(name: String): Boolean {
if (getter) {
return name.startsWith("get") || name.startsWith("is")
}
return name.startsWith("set")
}
override fun getRangeInElement() = TextRange(0, expression.textLength)
override fun canRename() = true
private fun renameByPropertyName(newName: String): PsiElement? {
val nameIdentifier = KtPsiFactory(expression).createNameIdentifier(newName)
expression.getReferencedNameElement().replace(nameIdentifier)
@@ -183,7 +172,4 @@ sealed class SyntheticPropertyAccessorReference(expression: KtNameReferenceExpre
override val resolvesByNames: Collection<Name>
get() = listOf(element.getReferencedNameAsName())
class Getter(expression: KtNameReferenceExpression) : SyntheticPropertyAccessorReference(expression, true)
class Setter(expression: KtNameReferenceExpression) : SyntheticPropertyAccessorReference(expression, false)
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2020 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.
*/
@@ -221,20 +221,20 @@ fun AbstractKtReference<out KtExpression>.renameImplicitConventionalCall(newName
return newExpression
}
val KtSimpleNameExpression.mainReference: KtSimpleNameReference
val KtSimpleNameExpression.mainReference: KtSimpleNameReferenceDescriptorsImpl
get() = references.firstIsInstance()
val KtReferenceExpression.mainReference: KtReference
get() = if (this is KtSimpleNameExpression) mainReference else references.firstIsInstance<KtReference>()
val KtReferenceExpression.mainReference: KtDescriptorsBasedReference
get() = if (this is KtSimpleNameExpression) mainReference else references.firstIsInstance<KtDescriptorsBasedReference>()
val KDocName.mainReference: KDocReference
get() = references.firstIsInstance()
val KtElement.mainReference: KtReference?
val KtElement.mainReference: KtDescriptorsBasedReference?
get() = when (this) {
is KtReferenceExpression -> mainReference
is KDocName -> mainReference
else -> references.firstIsInstanceOrNull<KtReference>()
else -> references.firstIsInstanceOrNull<KtDescriptorsBasedReference>()
}
fun KtElement.resolveMainReferenceToDescriptors(): Collection<DeclarationDescriptor> {
@@ -248,46 +248,6 @@ fun PsiReference.getImportAlias(): KtImportAlias? {
// ----------- Read/write access -----------------------------------------------------------------------------------------------------------------------
enum class ReferenceAccess(val isRead: Boolean, val isWrite: Boolean) {
READ(true, false), WRITE(false, true), READ_WRITE(true, true)
}
fun KtExpression.readWriteAccess(useResolveForReadWrite: Boolean) = readWriteAccessWithFullExpression(useResolveForReadWrite).first
fun KtExpression.readWriteAccessWithFullExpression(useResolveForReadWrite: Boolean): Pair<ReferenceAccess, KtExpression> {
var expression = 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) {
when (assignment.operationToken) {
KtTokens.EQ -> return ReferenceAccess.WRITE to assignment
else -> {
if (!useResolveForReadWrite) return ReferenceAccess.READ_WRITE to assignment
val resolvedCall = assignment.resolveToCall() ?: return ReferenceAccess.READ_WRITE to assignment
if (!resolvedCall.isReallySuccess()) return ReferenceAccess.READ_WRITE to assignment
return if (resolvedCall.resultingDescriptor.name in OperatorConventions.ASSIGNMENT_OPERATIONS.values)
ReferenceAccess.READ to assignment
else
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
}
fun KtReference.canBeResolvedViaImport(target: DeclarationDescriptor, bindingContext: BindingContext): Boolean {
if (this is KDocReference) {
val qualifier = element.getQualifier() ?: return true
@@ -1,22 +0,0 @@
/*
* Copyright 2010-2019 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?
}