From a7c37286a31ec52f249420c0e11ad171eec9de9a Mon Sep 17 00:00:00 2001 From: Tianyu Geng Date: Thu, 5 May 2022 16:59:19 +0000 Subject: [PATCH] [Analysis API] Move SyntheticPropertyAccessorReferenceDescriptorImpl Also move some utilities from IDE to kotlin repo --- analysis/analysis-api-fe10/build.gradle.kts | 1 + .../Fe10SyntheticPropertyAccessorReference.kt | 62 +++++++++++++++++++ ...tFe10KotlinReferenceProviderContributor.kt | 30 +++++++++ .../SyntheticPropertyAccessorReference.kt | 2 - .../kotlin/asJava/lightClassUtils.kt | 51 +++++++++++++++ 5 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/references/Fe10SyntheticPropertyAccessorReference.kt diff --git a/analysis/analysis-api-fe10/build.gradle.kts b/analysis/analysis-api-fe10/build.gradle.kts index 204582a24ae..b4436b6d8f4 100644 --- a/analysis/analysis-api-fe10/build.gradle.kts +++ b/analysis/analysis-api-fe10/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { implementation(project(":compiler:backend.jvm")) implementation(project(":compiler:backend-common")) implementation(project(":compiler:backend.common.jvm")) + implementation(project(":compiler:light-classes")) testApiJUnit5() testImplementation(project(":analysis:analysis-api-providers")) diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/references/Fe10SyntheticPropertyAccessorReference.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/references/Fe10SyntheticPropertyAccessorReference.kt new file mode 100644 index 00000000000..06c708cf20e --- /dev/null +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/references/Fe10SyntheticPropertyAccessorReference.kt @@ -0,0 +1,62 @@ +/* + * 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 + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.util.SmartList +import org.jetbrains.kotlin.analysis.api.descriptors.references.base.KtFe10Reference +import org.jetbrains.kotlin.asJava.canHaveSyntheticGetter +import org.jetbrains.kotlin.asJava.canHaveSyntheticSetter +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.idea.references.SyntheticPropertyAccessorReference +import org.jetbrains.kotlin.idea.references.readWriteAccess +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtImportAlias +import org.jetbrains.kotlin.psi.KtNameReferenceExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets +import org.jetbrains.kotlin.resolve.references.ReferenceAccess +import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor +import org.jetbrains.kotlin.utils.addIfNotNull + +class Fe10SyntheticPropertyAccessorReference( + expression: KtNameReferenceExpression, + getter: Boolean +) : SyntheticPropertyAccessorReference(expression, getter), KtFe10Reference { + + override fun canBeReferenceTo(candidateTarget: PsiElement): Boolean { + if (candidateTarget !is PsiMethod || !isAccessorName(candidateTarget.name)) return false + if (getter && !candidateTarget.canHaveSyntheticGetter || !getter && !candidateTarget.canHaveSyntheticSetter) return false + if (!getter && expression.readWriteAccess(true) == ReferenceAccess.READ) return false + return true + } + + override fun getTargetDescriptors(context: BindingContext): Collection { + val descriptors = expression.getReferenceTargets(context) + if (descriptors.none { it is SyntheticJavaPropertyDescriptor }) return emptyList() + + val result = SmartList() + for (descriptor in descriptors) { + if (descriptor is SyntheticJavaPropertyDescriptor) { + if (getter) { + result.add(descriptor.getMethod) + } else { + result.addIfNotNull(descriptor.setMethod) + } + } + } + return result + } + + override fun isReferenceToImportAlias(alias: KtImportAlias): Boolean { + return super.isReferenceToImportAlias(alias) + } + + override val resolvesByNames: Collection + get() = listOf(element.getReferencedNameAsName()) +} diff --git a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/references/base/KtFe10KotlinReferenceProviderContributor.kt b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/references/base/KtFe10KotlinReferenceProviderContributor.kt index 1339eadf71d..a488cb181af 100644 --- a/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/references/base/KtFe10KotlinReferenceProviderContributor.kt +++ b/analysis/analysis-api-fe10/src/org/jetbrains/kotlin/analysis/api/descriptors/references/base/KtFe10KotlinReferenceProviderContributor.kt @@ -5,9 +5,18 @@ package org.jetbrains.kotlin.analysis.api.descriptors.references.base +import com.intellij.psi.PsiReference import org.jetbrains.kotlin.analysis.api.descriptors.references.* import org.jetbrains.kotlin.idea.references.KotlinPsiReferenceRegistrar import org.jetbrains.kotlin.idea.references.KotlinReferenceProviderContributor +import org.jetbrains.kotlin.idea.references.readWriteAccess +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtImportDirective +import org.jetbrains.kotlin.psi.KtNameReferenceExpression +import org.jetbrains.kotlin.psi.KtPackageDirective +import org.jetbrains.kotlin.psi.KtUserType +import org.jetbrains.kotlin.psi.psiUtil.parents +import org.jetbrains.kotlin.resolve.references.ReferenceAccess class KtFe10KotlinReferenceProviderContributor : KotlinReferenceProviderContributor { override fun registerReferenceProviders(registrar: KotlinPsiReferenceRegistrar) { @@ -21,6 +30,27 @@ class KtFe10KotlinReferenceProviderContributor : KotlinReferenceProviderContribu registerProvider(factory = ::KtFe10ConstructorDelegationReference) registerProvider(factory = ::KtFe10CollectionLiteralReference) registerProvider(factory = ::Fe10KDocReference) + + registerMultiProvider { nameReferenceExpression -> + if (nameReferenceExpression.getReferencedNameElementType() != KtTokens.IDENTIFIER) { + return@registerMultiProvider PsiReference.EMPTY_ARRAY + } + if (nameReferenceExpression.parents.any { it is KtImportDirective || it is KtPackageDirective || it is KtUserType }) { + return@registerMultiProvider PsiReference.EMPTY_ARRAY + } + + when (nameReferenceExpression.readWriteAccess(useResolveForReadWrite = false)) { + ReferenceAccess.READ -> + arrayOf(Fe10SyntheticPropertyAccessorReference(nameReferenceExpression, getter = true)) + ReferenceAccess.WRITE -> + arrayOf(Fe10SyntheticPropertyAccessorReference(nameReferenceExpression, getter = false)) + ReferenceAccess.READ_WRITE -> + arrayOf( + Fe10SyntheticPropertyAccessorReference(nameReferenceExpression, getter = true), + Fe10SyntheticPropertyAccessorReference(nameReferenceExpression, getter = false) + ) + } + } } } } \ No newline at end of file diff --git a/analysis/kt-references/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt b/analysis/kt-references/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt index ddfa36022ab..edaabaeb701 100644 --- a/analysis/kt-references/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt +++ b/analysis/kt-references/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt @@ -25,8 +25,6 @@ abstract class SyntheticPropertyAccessorReference( override fun canRename() = true - abstract override fun handleElementRename(newElementName: String): PsiElement? - override val resolvesByNames: Collection get() = listOf(element.getReferencedNameAsName()) } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt index e96cf4c9f82..b739bd49289 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/lightClassUtils.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.asJava +import com.intellij.lang.jvm.JvmModifier import com.intellij.psi.* import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.asJava.classes.KtFakeLightClass @@ -18,6 +19,7 @@ import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName import org.jetbrains.kotlin.load.java.propertyNameBySetMethodName +import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* @@ -286,3 +288,52 @@ fun computeExpression(expression: PsiElement): Any? { if (constant.isError) return null return evalConstantValue(constant.toConstantValue(TypeUtils.NO_EXPECTED_TYPE)) } + +private val PsiMethod.canBeGetter: Boolean + get() = JvmAbi.isGetterName(name) && parameters.isEmpty() && returnTypeElement?.textMatches("void") != true + +private val PsiMethod.canBeSetter: Boolean + get() = JvmAbi.isSetterName(name) && parameters.size == 1 && returnTypeElement?.textMatches("void") != false + +private val PsiMethod.probablyCanHaveSyntheticAccessors: Boolean + get() = canHaveOverride && !hasTypeParameters() && !isFinalProperty + +private val PsiMethod.getterName: Name? get() = propertyNameByGetMethodName(Name.identifier(name)) +private val PsiMethod.setterNames: Collection? get() = propertyNamesBySetMethodName(Name.identifier(name)).takeIf { it.isNotEmpty() } + +private val PsiMethod.isFinalProperty: Boolean + get() { + val property = unwrapped as? KtProperty ?: return false + if (property.hasModifier(KtTokens.OVERRIDE_KEYWORD)) return false + val containingClassOrObject = property.containingClassOrObject ?: return true + return containingClassOrObject is KtObjectDeclaration + } + +private val PsiMethod.isTopLevelDeclaration: Boolean get() = unwrapped?.isTopLevelKtOrJavaMember() == true + +val PsiMethod.syntheticAccessors: Collection + get() { + if (!probablyCanHaveSyntheticAccessors) return emptyList() + + return when { + canBeGetter -> listOfNotNull(getterName) + canBeSetter -> setterNames.orEmpty() + else -> emptyList() + } + } + +val PsiMethod.canHaveSyntheticAccessors: Boolean get() = probablyCanHaveSyntheticAccessors && (canBeGetter || canBeSetter) + +val PsiMethod.canHaveSyntheticGetter: Boolean get() = probablyCanHaveSyntheticAccessors && canBeGetter + +val PsiMethod.canHaveSyntheticSetter: Boolean get() = probablyCanHaveSyntheticAccessors && canBeSetter + +val PsiMethod.syntheticGetter: Name? get() = if (canHaveSyntheticGetter) getterName else null + +val PsiMethod.syntheticSetters: Collection? get() = if (canHaveSyntheticSetter) setterNames else null + +/** + * Attention: only language constructs are checked. For example: static member, constructor, top-level property + * @return `false` if constraints are found. Otherwise, `true` + */ +val PsiMethod.canHaveOverride: Boolean get() = !hasModifier(JvmModifier.STATIC) && !isConstructor && !isTopLevelDeclaration