[FIR] Introduce FirAssignExpressionAltererExtension
This extension can be used to override resolution of assign statements with custom statement
This commit is contained in:
committed by
teamcity
parent
42b1621b4b
commit
bb38690273
+9
@@ -363,6 +363,15 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
add(FirErrors.AMBIGUOUS_ALTERED_ASSIGN) { firDiagnostic ->
|
||||||
|
AmbiguousAlteredAssignImpl(
|
||||||
|
firDiagnostic.a.map { string ->
|
||||||
|
string
|
||||||
|
},
|
||||||
|
firDiagnostic as KtPsiDiagnostic,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
add(FirErrors.SUPER_IS_NOT_AN_EXPRESSION) { firDiagnostic ->
|
add(FirErrors.SUPER_IS_NOT_AN_EXPRESSION) { firDiagnostic ->
|
||||||
SuperIsNotAnExpressionImpl(
|
SuperIsNotAnExpressionImpl(
|
||||||
firDiagnostic as KtPsiDiagnostic,
|
firDiagnostic as KtPsiDiagnostic,
|
||||||
|
|||||||
+5
@@ -291,6 +291,11 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
abstract val classSymbol: KtClassLikeSymbol
|
abstract val classSymbol: KtClassLikeSymbol
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class AmbiguousAlteredAssign : KtFirDiagnostic<PsiElement>() {
|
||||||
|
override val diagnosticClass get() = AmbiguousAlteredAssign::class
|
||||||
|
abstract val altererNames: List<String?>
|
||||||
|
}
|
||||||
|
|
||||||
abstract class SuperIsNotAnExpression : KtFirDiagnostic<PsiElement>() {
|
abstract class SuperIsNotAnExpression : KtFirDiagnostic<PsiElement>() {
|
||||||
override val diagnosticClass get() = SuperIsNotAnExpression::class
|
override val diagnosticClass get() = SuperIsNotAnExpression::class
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -336,6 +336,12 @@ internal class ResolutionToClassifierImpl(
|
|||||||
override val token: KtLifetimeToken,
|
override val token: KtLifetimeToken,
|
||||||
) : KtFirDiagnostic.ResolutionToClassifier(), KtAbstractFirDiagnostic<PsiElement>
|
) : KtFirDiagnostic.ResolutionToClassifier(), KtAbstractFirDiagnostic<PsiElement>
|
||||||
|
|
||||||
|
internal class AmbiguousAlteredAssignImpl(
|
||||||
|
override val altererNames: List<String?>,
|
||||||
|
override val firDiagnostic: KtPsiDiagnostic,
|
||||||
|
override val token: KtLifetimeToken,
|
||||||
|
) : KtFirDiagnostic.AmbiguousAlteredAssign(), KtAbstractFirDiagnostic<PsiElement>
|
||||||
|
|
||||||
internal class SuperIsNotAnExpressionImpl(
|
internal class SuperIsNotAnExpressionImpl(
|
||||||
override val firDiagnostic: KtPsiDiagnostic,
|
override val firDiagnostic: KtPsiDiagnostic,
|
||||||
override val token: KtLifetimeToken,
|
override val token: KtLifetimeToken,
|
||||||
|
|||||||
+3
@@ -142,6 +142,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
|||||||
val RESOLUTION_TO_CLASSIFIER by error<PsiElement> {
|
val RESOLUTION_TO_CLASSIFIER by error<PsiElement> {
|
||||||
parameter<FirRegularClassSymbol>("classSymbol")
|
parameter<FirRegularClassSymbol>("classSymbol")
|
||||||
}
|
}
|
||||||
|
val AMBIGUOUS_ALTERED_ASSIGN by error<PsiElement> {
|
||||||
|
parameter<List<String?>>("altererNames")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val SUPER by object : DiagnosticGroup("Super") {
|
val SUPER by object : DiagnosticGroup("Super") {
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ object FirErrors {
|
|||||||
val NO_RECEIVER_ALLOWED by error0<PsiElement>()
|
val NO_RECEIVER_ALLOWED by error0<PsiElement>()
|
||||||
val FUNCTION_EXPECTED by error2<PsiElement, String, ConeKotlinType>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
val FUNCTION_EXPECTED by error2<PsiElement, String, ConeKotlinType>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||||
val RESOLUTION_TO_CLASSIFIER by error1<PsiElement, FirRegularClassSymbol>()
|
val RESOLUTION_TO_CLASSIFIER by error1<PsiElement, FirRegularClassSymbol>()
|
||||||
|
val AMBIGUOUS_ALTERED_ASSIGN by error1<PsiElement, List<String?>>()
|
||||||
|
|
||||||
// Super
|
// Super
|
||||||
val SUPER_IS_NOT_AN_EXPRESSION by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
val SUPER_IS_NOT_AN_EXPRESSION by error0<PsiElement>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
|
||||||
|
|||||||
+7
@@ -56,6 +56,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_TYPE_ALIAS
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_TYPE_ALIAS_WITH_USE_SITE_VARIANCE
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_TYPE_ALIAS_WITH_USE_SITE_VARIANCE
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_WITHOUT_EXPECT
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ACTUAL_WITHOUT_EXPECT
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_ACTUALS
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_ACTUALS
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_ALTERED_ASSIGN
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_ANONYMOUS_TYPE_INFERRED
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_ANONYMOUS_TYPE_INFERRED
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_CALL_WITH_IMPLICIT_CONTEXT_RECEIVER
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_CALL_WITH_IMPLICIT_CONTEXT_RECEIVER
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_EXPECTS
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.AMBIGUOUS_EXPECTS
|
||||||
@@ -674,6 +675,12 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
|||||||
"Constructor of inner class {0} can be called only with receiver of containing class",
|
"Constructor of inner class {0} can be called only with receiver of containing class",
|
||||||
SYMBOL
|
SYMBOL
|
||||||
)
|
)
|
||||||
|
map.put(
|
||||||
|
AMBIGUOUS_ALTERED_ASSIGN,
|
||||||
|
"Multiple extensions tried to alter this assignement at the same time. Extensions: {0}",
|
||||||
|
COLLECTION(NULLABLE_STRING)
|
||||||
|
)
|
||||||
|
|
||||||
map.put(ILLEGAL_SELECTOR, "The expression cannot be a selector (occur after a dot)")
|
map.put(ILLEGAL_SELECTOR, "The expression cannot be a selector (occur after a dot)")
|
||||||
map.put(NO_RECEIVER_ALLOWED, "No receiver can be passed to this function or property")
|
map.put(NO_RECEIVER_ALLOWED, "No receiver can be passed to this function or property")
|
||||||
|
|
||||||
|
|||||||
+1
@@ -133,6 +133,7 @@ private fun ConeDiagnostic.toKtDiagnostic(
|
|||||||
is ConeUnderscoreUsageWithoutBackticks -> FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.createOn(this.source)
|
is ConeUnderscoreUsageWithoutBackticks -> FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.createOn(this.source)
|
||||||
is ConeAmbiguousSuper -> FirErrors.AMBIGUOUS_SUPER.createOn(source, this.candidateTypes)
|
is ConeAmbiguousSuper -> FirErrors.AMBIGUOUS_SUPER.createOn(source, this.candidateTypes)
|
||||||
is ConeUnresolvedParentInImport -> null // reported in FirUnresolvedImportChecker
|
is ConeUnresolvedParentInImport -> null // reported in FirUnresolvedImportChecker
|
||||||
|
is ConeAmbiguousAlteredAssign -> FirErrors.AMBIGUOUS_ALTERED_ASSIGN.createOn(source, this.altererNames)
|
||||||
else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}")
|
else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
@@ -29,6 +29,7 @@ abstract class FirExtensionRegistrar : FirExtensionRegistrarAdapter() {
|
|||||||
FirExpressionResolutionExtension::class,
|
FirExpressionResolutionExtension::class,
|
||||||
FirExtensionSessionComponent::class,
|
FirExtensionSessionComponent::class,
|
||||||
FirSamConversionTransformerExtension::class,
|
FirSamConversionTransformerExtension::class,
|
||||||
|
FirAssignExpressionAltererExtension::class,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,6 +78,11 @@ abstract class FirExtensionRegistrar : FirExtensionRegistrarAdapter() {
|
|||||||
registerExtension(FirSamConversionTransformerExtension::class, this)
|
registerExtension(FirSamConversionTransformerExtension::class, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmName("plusAssignExpressionAltererExtension")
|
||||||
|
operator fun (FirAssignExpressionAltererExtension.Factory).unaryPlus() {
|
||||||
|
registerExtension(FirAssignExpressionAltererExtension::class, this)
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------ reference methods ------------------
|
// ------------------ reference methods ------------------
|
||||||
|
|
||||||
@JvmName("plusStatusTransformerExtension")
|
@JvmName("plusStatusTransformerExtension")
|
||||||
@@ -119,6 +125,11 @@ abstract class FirExtensionRegistrar : FirExtensionRegistrarAdapter() {
|
|||||||
FirSamConversionTransformerExtension.Factory { this.invoke(it) }.unaryPlus()
|
FirSamConversionTransformerExtension.Factory { this.invoke(it) }.unaryPlus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmName("plusAssignExpressionAltererExtension")
|
||||||
|
operator fun ((FirSession) -> FirAssignExpressionAltererExtension).unaryPlus() {
|
||||||
|
FirAssignExpressionAltererExtension.Factory { this.invoke(it) }.unaryPlus()
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------ utilities ------------------
|
// ------------------ utilities ------------------
|
||||||
|
|
||||||
@JvmName("bindLeft")
|
@JvmName("bindLeft")
|
||||||
|
|||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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.fir.extensions
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
abstract class FirAssignExpressionAltererExtension(session: FirSession) : FirExtension(session) {
|
||||||
|
companion object {
|
||||||
|
val NAME = FirExtensionPointName("AssignAlterer")
|
||||||
|
}
|
||||||
|
|
||||||
|
final override val name: FirExtensionPointName
|
||||||
|
get() = NAME
|
||||||
|
|
||||||
|
final override val extensionType: KClass<out FirExtension> = FirAssignExpressionAltererExtension::class
|
||||||
|
|
||||||
|
/**
|
||||||
|
* At this point [variableAssignment] contains resolved and completed lhs and calleeReference(lvalue)
|
||||||
|
* and unresolved rValue expression
|
||||||
|
*
|
||||||
|
* It's allowed to transform [variableAssignment] into any kind of statement. This state should be unresolved
|
||||||
|
* (modulo usages of already resolved parts, like lValue). Later this statement will be resolved by compiler
|
||||||
|
* itself using regular resolution algorithms
|
||||||
|
*/
|
||||||
|
abstract fun transformVariableAssignment(variableAssignment: FirVariableAssignment): FirStatement?
|
||||||
|
|
||||||
|
fun interface Factory : FirExtension.Factory<FirAssignExpressionAltererExtension>
|
||||||
|
}
|
||||||
|
|
||||||
|
val FirExtensionService.assignAltererExtensions: List<FirAssignExpressionAltererExtension> by FirExtensionService.registeredExtensions()
|
||||||
+26
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.expressions.*
|
|||||||
import org.jetbrains.kotlin.fir.expressions.builder.*
|
import org.jetbrains.kotlin.fir.expressions.builder.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
|
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
|
||||||
import org.jetbrains.kotlin.fir.expressions.impl.toAnnotationArgumentMapping
|
import org.jetbrains.kotlin.fir.expressions.impl.toAnnotationArgumentMapping
|
||||||
|
import org.jetbrains.kotlin.fir.extensions.assignAltererExtensions
|
||||||
import org.jetbrains.kotlin.fir.extensions.expressionResolutionExtensions
|
import org.jetbrains.kotlin.fir.extensions.expressionResolutionExtensions
|
||||||
import org.jetbrains.kotlin.fir.extensions.extensionService
|
import org.jetbrains.kotlin.fir.extensions.extensionService
|
||||||
import org.jetbrains.kotlin.fir.references.*
|
import org.jetbrains.kotlin.fir.references.*
|
||||||
@@ -60,6 +61,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
|
|||||||
var containingSafeCallExpression: FirSafeCallExpression? = null
|
var containingSafeCallExpression: FirSafeCallExpression? = null
|
||||||
|
|
||||||
private val expressionResolutionExtensions = session.extensionService.expressionResolutionExtensions.takeIf { it.isNotEmpty() }
|
private val expressionResolutionExtensions = session.extensionService.expressionResolutionExtensions.takeIf { it.isNotEmpty() }
|
||||||
|
private val assignAltererExtensions = session.extensionService.assignAltererExtensions.takeIf { it.isNotEmpty() }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@Suppress("LeakingThis")
|
@Suppress("LeakingThis")
|
||||||
@@ -845,6 +847,30 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
|
|||||||
val resolvedAssignment = callResolver.resolveVariableAccessAndSelectCandidate(variableAssignment, isUsedAsReceiver = false)
|
val resolvedAssignment = callResolver.resolveVariableAccessAndSelectCandidate(variableAssignment, isUsedAsReceiver = false)
|
||||||
val result = if (resolvedAssignment is FirVariableAssignment) {
|
val result = if (resolvedAssignment is FirVariableAssignment) {
|
||||||
val completeAssignment = callCompleter.completeCall(resolvedAssignment, noExpectedType).result // TODO: check
|
val completeAssignment = callCompleter.completeCall(resolvedAssignment, noExpectedType).result // TODO: check
|
||||||
|
val resolvedReference = completeAssignment.calleeReference
|
||||||
|
if (assignAltererExtensions != null && resolvedReference is FirResolvedNamedReference) {
|
||||||
|
val alteredAssignments = assignAltererExtensions.mapNotNull { alterer ->
|
||||||
|
alterer.transformVariableAssignment(variableAssignment)?.let { it to alterer }
|
||||||
|
}
|
||||||
|
when (alteredAssignments.size) {
|
||||||
|
0 -> {}
|
||||||
|
1 -> {
|
||||||
|
val transformedAssignment = alteredAssignments.first().first
|
||||||
|
return transformedAssignment.transform(transformer, ResolutionMode.ContextIndependent)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
val altererNames = alteredAssignments.map { it.second::class.qualifiedName }
|
||||||
|
val errorReference = buildErrorNamedReference {
|
||||||
|
source = resolvedReference.source
|
||||||
|
candidateSymbol = resolvedReference.resolvedSymbol
|
||||||
|
diagnostic = ConeAmbiguousAlteredAssign(altererNames)
|
||||||
|
}
|
||||||
|
completeAssignment.replaceCalleeReference(errorReference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
completeAssignment.transformRValue(
|
completeAssignment.transformRValue(
|
||||||
transformer,
|
transformer,
|
||||||
withExpectedType(variableAssignment.lValueTypeRef, expectedTypeMismatchIsReportedInChecker = true),
|
withExpectedType(variableAssignment.lValueTypeRef, expectedTypeMismatchIsReportedInChecker = true),
|
||||||
|
|||||||
+5
-1
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
|||||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||||
import org.jetbrains.kotlin.fir.types.renderForDebugging
|
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
|
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
|
||||||
@@ -233,3 +232,8 @@ private fun describeSymbol(symbol: FirBasedSymbol<*>): String {
|
|||||||
else -> "$symbol"
|
else -> "$symbol"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ConeAmbiguousAlteredAssign(val altererNames: List<String?>) : ConeDiagnostic {
|
||||||
|
override val reason: String
|
||||||
|
get() = "Assign altered by multiple extensions"
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user