KT-57468 Kotlin assignment plugin: operation name cannot be found

The problem results in broken import quick fix and import optimizer on
the IDE side [1].

`AssignResolutionAltererExtension` introduced a possibility to override
 resolution of assignment statements. The inconsistency though is
 that `KtSimpleNameReference.getResolvesByNames` doesn't return a name
 for the overridden `=`. Kotlin as a language doesn't support this [2].

This commit eliminates the drawback above:
1. It fixes the name `assign` the `=` can be resolved to [3].
   This eliminates the need to search for the name, bypassing the
   plugins.
2. `KtSimpleNameReference.getResolvesByNames` returns `assign` among
   other names in case it deals with binary `=` and assignment is
   resolved.
3. `KtCompilerPluginsProvider` was extended to check plugins' presence.
   K1 implementation added.

----------------------------------------------------------------
[1]: https://youtrack.jetbrains.com/issue/KTIJ-24390
[2]: OperatorConventions.getNameForOperationSymbol
     https://kotlinlang.org/docs/operator-overloading.html#augmented-assignments
[3]: OperatorConventions#ASSIGN_METHOD + AssignmentPluginNames
This commit is contained in:
Andrei Klunnyi
2023-03-08 10:57:51 +01:00
committed by Space Team
parent 4c1a66b7d6
commit 1e0115aef8
16 changed files with 83 additions and 22 deletions
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2023 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
import org.jetbrains.kotlin.analysis.project.structure.KtCompilerPluginsProvider
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
import org.jetbrains.kotlin.extensions.internal.InternalNonStableExtensionPoints
import org.jetbrains.kotlin.resolve.extensions.AssignResolutionAltererExtension
@Suppress("unused")
class KtFe10CompilerPluginsProvider : KtCompilerPluginsProvider() {
override fun <T : Any> getRegisteredExtensions(module: KtSourceModule, extensionType: ProjectExtensionDescriptor<T>): List<T> {
return extensionType.getInstances(module.project)
}
@OptIn(InternalNonStableExtensionPoints::class)
override fun isPluginOfTypeRegistered(module: KtSourceModule, pluginType: CompilerPluginType): Boolean {
val extension = when (pluginType) {
CompilerPluginType.ASSIGNMENT -> AssignResolutionAltererExtension
else -> return false
}
return extension.getInstances(module.project).isNotEmpty()
}
}
@@ -15,6 +15,7 @@ dependencies {
api(project(":compiler:fir:checkers:checkers.js"))
api(project(":compiler:fir:checkers:checkers.native"))
api(project(":compiler:fir:java"))
api(project(":compiler:fir:entrypoint"))
api(project(":analysis:low-level-api-fir"))
api(project(":analysis:analysis-api"))
api(project(":analysis:analysis-api-impl-base"))
+2
View File
@@ -7,6 +7,8 @@ dependencies {
implementation(project(":compiler:psi"))
implementation(project(":analysis:light-classes-base"))
implementation(intellijCore())
implementation(project(":analysis:analysis-api-providers"))
implementation(project(":analysis:project-structure"))
compileOnly(commonDependency("com.google.guava:guava"))
}
@@ -7,14 +7,14 @@ package org.jetbrains.kotlin.references.fe10
import com.intellij.psi.PsiElement
import com.intellij.util.SmartList
import org.jetbrains.kotlin.references.fe10.base.KtFe10Reference
import org.jetbrains.kotlin.references.fe10.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.*
import org.jetbrains.kotlin.references.fe10.base.KtFe10Reference
import org.jetbrains.kotlin.references.fe10.base.KtFe10ReferenceResolutionHelper
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
@@ -5,8 +5,12 @@
package org.jetbrains.kotlin.idea.references
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.project.structure.KtCompilerPluginsProvider
import org.jetbrains.kotlin.analysis.project.structure.KtCompilerPluginsProvider.CompilerPluginType
import org.jetbrains.kotlin.analysis.project.structure.KtSourceModule
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -14,6 +18,9 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.types.expressions.OperatorConventions.ASSIGN_METHOD
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlin.analysis.project.structure.getKtModule
abstract class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleReference<KtSimpleNameExpression>(expression) {
// Extension point used by deprecated android extensions.
@@ -65,7 +72,12 @@ abstract class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSim
if (tokenType != null) {
val name = OperatorConventions.getNameForOperationSymbol(
tokenType, element.parent is KtUnaryExpression, element.parent is KtBinaryExpression
) ?: return emptyList()
)
?: (expression.parent as? KtBinaryExpression)?.let {
runIf(it.operationToken == KtTokens.EQ && isAssignmentResolved(element.project, it)) { ASSIGN_METHOD }
}
?: return emptyList()
val counterpart = OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS[tokenType]
return if (counterpart != null) {
val counterpartName = OperatorConventions.getNameForOperationSymbol(counterpart, false, true)!!
@@ -80,4 +92,13 @@ abstract class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSim
}
abstract fun getImportAlias(): KtImportAlias?
private fun isAssignmentResolved(project: Project, binaryExpression: KtBinaryExpression): Boolean {
val sourceModule = binaryExpression.getKtModule(element.project) as? KtSourceModule ?: return false
val reference = binaryExpression.operationReference.reference ?: return false
val pluginPresenceService = project.getService(KtCompilerPluginsProvider::class.java)
?: error("KtAssignResolutionPresenceService is not available as a service")
return pluginPresenceService.isPluginOfTypeRegistered(sourceModule, CompilerPluginType.ASSIGNMENT)
&& (reference.resolve() as? KtNamedFunction)?.nameAsName == ASSIGN_METHOD
}
}
@@ -12,4 +12,6 @@ import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
internal object NoOpKtCompilerPluginsProvider : KtCompilerPluginsProvider() {
override fun <T : Any> getRegisteredExtensions(module: KtSourceModule, extensionType: ProjectExtensionDescriptor<T>): List<T> =
emptyList()
override fun isPluginOfTypeRegistered(module: KtSourceModule, pluginType: CompilerPluginType): Boolean = false
}
@@ -11,10 +11,20 @@ import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
* A service which can return extensions which are registered for some module
*/
public abstract class KtCompilerPluginsProvider {
enum class CompilerPluginType {
ASSIGNMENT
}
/**
* Returns a list of extensions of a base [extensionType] which are registered for [module]
*
* These extensions are used in addition to those provided by the extension descriptor's [ProjectExtensionDescriptor.getInstances].
*/
public abstract fun <T : Any> getRegisteredExtensions(module: KtSourceModule, extensionType: ProjectExtensionDescriptor<T>): List<T>
/**
* Returns `true` if at least one plugin with requested `pluginType` is registered, `false` otherwise
*/
public abstract fun isPluginOfTypeRegistered(module: KtSourceModule, pluginType: CompilerPluginType): Boolean
}
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.types.expressions.ExpressionTypingComponents
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo
@InternalNonStableExtensionPoints
interface AssignResolutionAltererExtension : AnnotationBasedExtension {
companion object : ProjectExtensionDescriptor<AssignResolutionAltererExtension>(
@@ -104,6 +104,8 @@ public class OperatorConventions {
.put(KtTokens.MINUSEQ, KtTokens.MINUS)
.build();
public static final Name ASSIGN_METHOD = Name.identifier("assign");
public static final ImmutableBiMap<KtSingleValueToken, Name> BOOLEAN_OPERATIONS = ImmutableBiMap.<KtSingleValueToken, Name>builder()
.put(KtTokens.ANDAND, AND)
.put(KtTokens.OROR, OR)
@@ -5,10 +5,7 @@
package org.jetbrains.kotlin.assignment.plugin
import org.jetbrains.kotlin.name.Name
object AssignmentPluginNames {
const val PLUGIN_ID = "org.jetbrains.kotlin.assignment"
const val ANNOTATION_OPTION_NAME = "annotation"
val ASSIGN_METHOD = Name.identifier("assign")
}
@@ -5,10 +5,9 @@
package org.jetbrains.kotlin.assignment.plugin
import org.jetbrains.kotlin.cfg.getElementParentDeclaration
import org.jetbrains.kotlin.assignment.plugin.AssignmentPluginNames.ASSIGN_METHOD
import org.jetbrains.kotlin.assignment.plugin.diagnostics.ErrorsAssignmentPlugin.CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT
import org.jetbrains.kotlin.assignment.plugin.diagnostics.ErrorsAssignmentPlugin.NO_APPLICABLE_ASSIGN_METHOD
import org.jetbrains.kotlin.cfg.getElementParentDeclaration
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
@@ -29,6 +28,7 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.expressions.ExpressionTypingComponents
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo
import org.jetbrains.kotlin.types.expressions.OperatorConventions.ASSIGN_METHOD
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import java.util.*
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.assignment.plugin.diagnostics
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.assignment.plugin.AssignmentPluginNames.ASSIGN_METHOD
import org.jetbrains.kotlin.assignment.plugin.diagnostics.ErrorsAssignmentPlugin.DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
@@ -19,6 +18,7 @@ import org.jetbrains.kotlin.psi.KtModifierListOwner
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.types.expressions.OperatorConventions.ASSIGN_METHOD
class AssignmentPluginDeclarationChecker(private val annotations: List<String>) : DeclarationChecker {
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.assignment.plugin.k2
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.assignment.plugin.AssignmentPluginNames.ASSIGN_METHOD
import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.expressions.*
@@ -19,7 +18,9 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirBackingFieldSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFieldSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
import org.jetbrains.kotlin.fir.types.upperBoundIfFlexible
import org.jetbrains.kotlin.types.expressions.OperatorConventions.ASSIGN_METHOD
import org.jetbrains.kotlin.utils.addToStdlib.runIf
class FirAssignmentPluginAssignAltererExtension(
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.assignment.plugin.k2.diagnostics
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.assignment.plugin.AssignmentPluginNames.ASSIGN_METHOD
import org.jetbrains.kotlin.assignment.plugin.k2.annotationMatchingService
import org.jetbrains.kotlin.assignment.plugin.k2.diagnostics.FirErrorsAssignmentPlugin.CALL_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT
import org.jetbrains.kotlin.assignment.plugin.k2.diagnostics.FirErrorsAssignmentPlugin.NO_APPLICABLE_ASSIGN_METHOD
@@ -17,18 +16,15 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.FirDiagnosticHolder
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.arguments
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedErrorReference
import org.jetbrains.kotlin.fir.references.isError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeAmbiguityError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDiagnosticWithSingleCandidate
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
import org.jetbrains.kotlin.fir.types.isUnit
import org.jetbrains.kotlin.types.expressions.OperatorConventions.ASSIGN_METHOD
object FirAssignmentPluginFunctionCallChecker : FirFunctionCallChecker() {
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.assignment.plugin.k2.diagnostics
import org.jetbrains.kotlin.assignment.plugin.AssignmentPluginNames.ASSIGN_METHOD
import org.jetbrains.kotlin.assignment.plugin.k2.annotationMatchingService
import org.jetbrains.kotlin.assignment.plugin.k2.diagnostics.FirErrorsAssignmentPlugin.DECLARATION_ERROR_ASSIGN_METHOD_SHOULD_RETURN_UNIT
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
@@ -19,6 +18,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.isExtension
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isUnit
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
import org.jetbrains.kotlin.types.expressions.OperatorConventions.ASSIGN_METHOD
object FirAssignmentPluginFunctionChecker : FirSimpleFunctionChecker() {