[FIR] Disallow operators not on functions

Introduce `NOT_FUNCTION_AS_OPERATOR` and use it instead of `PROPERTY_AS_OPERATOR`

^KT-65881 Fixed

Merge-request: KT-MR-14547
This commit is contained in:
Ivan Kochurkin
2024-03-08 10:07:23 +00:00
committed by Space Team
parent df9d59851d
commit 2d4f4b9bb5
31 changed files with 440 additions and 100 deletions
@@ -124,15 +124,12 @@ class CandidateFactory private constructor(
result.addDiagnostic(NoCompanionObject)
}
if (callInfo.origin == FirFunctionCallOrigin.Operator) {
val propertySymbol = when {
symbol is FirPropertySymbol -> symbol
callInfo.candidateForCommonInvokeReceiver != null -> callInfo.candidateForCommonInvokeReceiver.symbol as? FirPropertySymbol
else -> null
}
if (propertySymbol != null) {
// Flag all property references that are resolved from an convention operator call.
result.addDiagnostic(PropertyAsOperator(propertySymbol))
val normalizedSymbol = when (symbol) {
!is FirFunctionSymbol -> symbol
else -> callInfo.candidateForCommonInvokeReceiver?.symbol?.takeIf { it !is FirFunctionSymbol }
}
// Flag all references that are resolved from an convention operator call.
normalizedSymbol?.let { result.addDiagnostic(NotFunctionAsOperator(normalizedSymbol)) }
}
if (symbol is FirPropertySymbol &&
!context.session.languageVersionSettings.supportsFeature(LanguageFeature.PrioritizedEnumEntries)
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.expressions.builder.FirPropertyAccessExpressionBuilder
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConePropertyAsOperator
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeNotFunctionAsOperator
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.runIf
internal class FirInvokeResolveTowerExtension(
private val context: ResolutionContext,
@@ -288,18 +289,26 @@ private fun BodyResolveComponents.createExplicitReceiverForInvoke(
invokeBuiltinExtensionMode: Boolean,
extensionReceiverExpression: FirExpression?
): FirExpression? {
val notFunctionAsOperatorDiagnostics = runIf (candidate.currentApplicability == CandidateApplicability.K2_NOT_FUNCTION_AS_OPERATOR) {
candidate.diagnostics.filterIsInstance<NotFunctionAsOperator>().map { ConeNotFunctionAsOperator(it.symbol) }
} ?: emptyList()
return when (val symbol = candidate.symbol) {
is FirCallableSymbol<*> -> createExplicitReceiverForInvokeByCallable(
candidate, info, invokeBuiltinExtensionMode, extensionReceiverExpression, symbol
candidate, info, invokeBuiltinExtensionMode, extensionReceiverExpression, symbol, notFunctionAsOperatorDiagnostics
)
is FirRegularClassSymbol -> buildResolvedQualifierForClass(
symbol,
sourceElement = info.fakeSourceForImplicitInvokeCallReceiver
sourceElement = info.fakeSourceForImplicitInvokeCallReceiver,
nonFatalDiagnostics = notFunctionAsOperatorDiagnostics,
)
is FirTypeAliasSymbol -> {
val type = symbol.fir.expandedTypeRef.coneTypeUnsafe<ConeClassLikeType>().fullyExpandedType(session)
val expansionRegularClassSymbol = type.lookupTag.toSymbol(session) ?: return null
buildResolvedQualifierForClass(expansionRegularClassSymbol, sourceElement = symbol.fir.source)
buildResolvedQualifierForClass(
expansionRegularClassSymbol,
sourceElement = symbol.fir.source,
nonFatalDiagnostics = notFunctionAsOperatorDiagnostics,
)
}
else -> throw AssertionError()
}
@@ -310,7 +319,8 @@ private fun BodyResolveComponents.createExplicitReceiverForInvokeByCallable(
info: CallInfo,
invokeBuiltinExtensionMode: Boolean,
extensionReceiverExpression: FirExpression?,
symbol: FirCallableSymbol<*>
symbol: FirCallableSymbol<*>,
nonFatalDiagnostics: List<ConeNotFunctionAsOperator>,
): FirExpression {
return FirPropertyAccessExpressionBuilder().apply {
val fakeSource = info.fakeSourceForImplicitInvokeCallReceiver
@@ -337,9 +347,7 @@ private fun BodyResolveComponents.createExplicitReceiverForInvokeByCallable(
explicitReceiver = info.explicitReceiver
}
if (candidate.currentApplicability == CandidateApplicability.K2_PROPERTY_AS_OPERATOR) {
nonFatalDiagnostics.add(ConePropertyAsOperator(candidate.symbol as FirPropertySymbol))
}
this.nonFatalDiagnostics.addAll(nonFatalDiagnostics)
candidate.updateSourcesOfReceivers()
@@ -170,11 +170,11 @@ class FirCallCompletionResultsWriterTransformer(
qualifiedAccessExpression.replaceContextReceiverArguments(subCandidate.contextReceiverArguments())
subCandidate.diagnostics.firstIsInstanceOrNull<PropertyAsOperator>()?.let { propertyAsOperator ->
val conePropertyAsOperator = ConePropertyAsOperator(propertyAsOperator.propertySymbol)
subCandidate.diagnostics.firstIsInstanceOrNull<NotFunctionAsOperator>()?.let { propertyAsOperator ->
val coneNotFunctionAsOperator = ConeNotFunctionAsOperator(propertyAsOperator.symbol)
val nonFatalDiagnostics: List<ConeDiagnostic> = buildList {
addAll(qualifiedAccessExpression.nonFatalDiagnostics)
add(conePropertyAsOperator)
add(coneNotFunctionAsOperator)
}
qualifiedAccessExpression.replaceNonFatalDiagnostics(nonFatalDiagnostics)
}