diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index 1646478b495..98410fcd7c5 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -363,6 +363,15 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert 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 -> SuperIsNotAnExpressionImpl( firDiagnostic as KtPsiDiagnostic, diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 66d5096006d..266d871baf9 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -291,6 +291,11 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val classSymbol: KtClassLikeSymbol } + abstract class AmbiguousAlteredAssign : KtFirDiagnostic() { + override val diagnosticClass get() = AmbiguousAlteredAssign::class + abstract val altererNames: List + } + abstract class SuperIsNotAnExpression : KtFirDiagnostic() { override val diagnosticClass get() = SuperIsNotAnExpression::class } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index a5fc6f1d640..426f6aac096 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -336,6 +336,12 @@ internal class ResolutionToClassifierImpl( override val token: KtLifetimeToken, ) : KtFirDiagnostic.ResolutionToClassifier(), KtAbstractFirDiagnostic +internal class AmbiguousAlteredAssignImpl( + override val altererNames: List, + override val firDiagnostic: KtPsiDiagnostic, + override val token: KtLifetimeToken, +) : KtFirDiagnostic.AmbiguousAlteredAssign(), KtAbstractFirDiagnostic + internal class SuperIsNotAnExpressionImpl( override val firDiagnostic: KtPsiDiagnostic, override val token: KtLifetimeToken, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index d1ee88eec3a..86e0f363deb 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -142,6 +142,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val RESOLUTION_TO_CLASSIFIER by error { parameter("classSymbol") } + val AMBIGUOUS_ALTERED_ASSIGN by error { + parameter>("altererNames") + } } val SUPER by object : DiagnosticGroup("Super") { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 2144bdf56f7..de8285adbe6 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -160,6 +160,7 @@ object FirErrors { val NO_RECEIVER_ALLOWED by error0() val FUNCTION_EXPECTED by error2(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) val RESOLUTION_TO_CLASSIFIER by error1() + val AMBIGUOUS_ALTERED_ASSIGN by error1>() // Super val SUPER_IS_NOT_AN_EXPRESSION by error0(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt index 6b6da0a2124..2b6994bf1af 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrorsDefaultMessages.kt @@ -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_WITHOUT_EXPECT 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_CALL_WITH_IMPLICIT_CONTEXT_RECEIVER 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", 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(NO_RECEIVER_ALLOWED, "No receiver can be passed to this function or property") diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt index 9fb6391fc37..fcc6a7917f5 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/coneDiagnosticToFirDiagnostic.kt @@ -133,6 +133,7 @@ private fun ConeDiagnostic.toKtDiagnostic( is ConeUnderscoreUsageWithoutBackticks -> FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.createOn(this.source) is ConeAmbiguousSuper -> FirErrors.AMBIGUOUS_SUPER.createOn(source, this.candidateTypes) is ConeUnresolvedParentInImport -> null // reported in FirUnresolvedImportChecker + is ConeAmbiguousAlteredAssign -> FirErrors.AMBIGUOUS_ALTERED_ASSIGN.createOn(source, this.altererNames) else -> throw IllegalArgumentException("Unsupported diagnostic type: ${this.javaClass}") } diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/extensions/FirExtensionRegistrar.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/extensions/FirExtensionRegistrar.kt index 1c27cbb90e9..aafafbee7c2 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/extensions/FirExtensionRegistrar.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/extensions/FirExtensionRegistrar.kt @@ -29,6 +29,7 @@ abstract class FirExtensionRegistrar : FirExtensionRegistrarAdapter() { FirExpressionResolutionExtension::class, FirExtensionSessionComponent::class, FirSamConversionTransformerExtension::class, + FirAssignExpressionAltererExtension::class, ) } @@ -77,6 +78,11 @@ abstract class FirExtensionRegistrar : FirExtensionRegistrarAdapter() { registerExtension(FirSamConversionTransformerExtension::class, this) } + @JvmName("plusAssignExpressionAltererExtension") + operator fun (FirAssignExpressionAltererExtension.Factory).unaryPlus() { + registerExtension(FirAssignExpressionAltererExtension::class, this) + } + // ------------------ reference methods ------------------ @JvmName("plusStatusTransformerExtension") @@ -119,6 +125,11 @@ abstract class FirExtensionRegistrar : FirExtensionRegistrarAdapter() { FirSamConversionTransformerExtension.Factory { this.invoke(it) }.unaryPlus() } + @JvmName("plusAssignExpressionAltererExtension") + operator fun ((FirSession) -> FirAssignExpressionAltererExtension).unaryPlus() { + FirAssignExpressionAltererExtension.Factory { this.invoke(it) }.unaryPlus() + } + // ------------------ utilities ------------------ @JvmName("bindLeft") diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirAssignExpressionAltererExtension.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirAssignExpressionAltererExtension.kt new file mode 100644 index 00000000000..4508f568a99 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirAssignExpressionAltererExtension.kt @@ -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 = 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 +} + +val FirExtensionService.assignAltererExtensions: List by FirExtensionService.registeredExtensions() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index e581eb37cef..6505d62cca6 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.builder.* import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList 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.extensionService import org.jetbrains.kotlin.fir.references.* @@ -60,6 +61,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform var containingSafeCallExpression: FirSafeCallExpression? = null private val expressionResolutionExtensions = session.extensionService.expressionResolutionExtensions.takeIf { it.isNotEmpty() } + private val assignAltererExtensions = session.extensionService.assignAltererExtensions.takeIf { it.isNotEmpty() } init { @Suppress("LeakingThis") @@ -845,6 +847,30 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform val resolvedAssignment = callResolver.resolveVariableAccessAndSelectCandidate(variableAssignment, isUsedAsReceiver = false) val result = if (resolvedAssignment is FirVariableAssignment) { 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( transformer, withExpectedType(variableAssignment.lValueTypeRef, expectedTypeMismatchIsReportedInChecker = true), diff --git a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt index d4aca7c4517..bd7b895c8f1 100644 --- a/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt +++ b/compiler/fir/semantics/src/org/jetbrains/kotlin/fir/resolve/diagnostics/ConeDiagnostics.kt @@ -16,7 +16,6 @@ import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.ConeKotlinType 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.Name import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability @@ -233,3 +232,8 @@ private fun describeSymbol(symbol: FirBasedSymbol<*>): String { else -> "$symbol" } } + +class ConeAmbiguousAlteredAssign(val altererNames: List) : ConeDiagnostic { + override val reason: String + get() = "Assign altered by multiple extensions" +}