[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
@@ -1585,8 +1585,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<FirNamedFunctionSymbol>("functionSymbol")
parameter<String>("operator")
}
val PROPERTY_AS_OPERATOR by error<PsiElement>(PositioningStrategy.OPERATOR) {
parameter<FirPropertySymbol>("property")
val NOT_FUNCTION_AS_OPERATOR by error<PsiElement>(PositioningStrategy.OPERATOR) {
parameter<String>("elementName")
parameter<FirBasedSymbol<*>>("elementSymbol")
}
val DSL_SCOPE_VIOLATION by error<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) {
parameter<FirBasedSymbol<*>>("calleeSymbol")
@@ -796,7 +796,7 @@ object FirErrors {
val IMPLICIT_BOXING_IN_IDENTITY_EQUALS: KtDiagnosticFactory2<ConeKotlinType, ConeKotlinType> by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
val INC_DEC_SHOULD_NOT_RETURN_UNIT: KtDiagnosticFactory0 by error0<KtExpression>(SourceElementPositioningStrategies.OPERATOR)
val ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT: KtDiagnosticFactory2<FirNamedFunctionSymbol, String> by error2<KtExpression, FirNamedFunctionSymbol, String>(SourceElementPositioningStrategies.OPERATOR)
val PROPERTY_AS_OPERATOR: KtDiagnosticFactory1<FirPropertySymbol> by error1<PsiElement, FirPropertySymbol>(SourceElementPositioningStrategies.OPERATOR)
val NOT_FUNCTION_AS_OPERATOR: KtDiagnosticFactory2<String, FirBasedSymbol<*>> by error2<PsiElement, String, FirBasedSymbol<*>>(SourceElementPositioningStrategies.OPERATOR)
val DSL_SCOPE_VIOLATION: KtDiagnosticFactory1<FirBasedSymbol<*>> by error1<PsiElement, FirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
// Type alias
@@ -533,7 +533,7 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set<String> = setOf(
"FORBIDDEN_IDENTITY_EQUALS",
"INC_DEC_SHOULD_NOT_RETURN_UNIT",
"ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT",
"PROPERTY_AS_OPERATOR",
"NOT_FUNCTION_AS_OPERATOR",
"DSL_SCOPE_VIOLATION",
"TOPLEVEL_TYPEALIASES_ONLY",
"RECURSIVE_TYPEALIAS_EXPANSION",
@@ -11,23 +11,21 @@ import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
import org.jetbrains.kotlin.fir.expressions.unwrapSmartcastExpression
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConePropertyAsOperator
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeNotFunctionAsOperator
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.ConeDynamicType
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.resolvedType
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
object FirConventionFunctionCallChecker : FirFunctionCallChecker(MppCheckerKind.Common) {
override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
// PROPERTY_AS_OPERATOR can only happen for function calls and it's reported on the receiver expression.
checkPropertyAsOperator(expression, expression.dispatchReceiver, context, reporter)
checkPropertyAsOperator(expression, expression.extensionReceiver, context, reporter)
checkNotFunctionAsOperator(expression, expression.dispatchReceiver, context, reporter)
checkNotFunctionAsOperator(expression, expression.extensionReceiver, context, reporter)
val calleeReference = expression.calleeReference as? FirErrorNamedReference ?: return
val diagnostic = calleeReference.diagnostic as? ConeUnresolvedNameError ?: return
@@ -39,7 +37,7 @@ object FirConventionFunctionCallChecker : FirFunctionCallChecker(MppCheckerKind.
}
}
private fun checkPropertyAsOperator(
private fun checkNotFunctionAsOperator(
callExpression: FirFunctionCall,
receiver: FirExpression?,
context: CheckerContext,
@@ -47,9 +45,27 @@ object FirConventionFunctionCallChecker : FirFunctionCallChecker(MppCheckerKind.
) {
if (callExpression.dispatchReceiver?.resolvedType is ConeDynamicType) return
// KT-61905: TODO: Return also in case of error type.
val unwrapped = receiver?.unwrapSmartcastExpression()
if (unwrapped !is FirPropertyAccessExpression) return
val diagnostic = unwrapped.nonFatalDiagnostics.firstIsInstanceOrNull<ConePropertyAsOperator>() ?: return
reporter.reportOn(callExpression.calleeReference.source, FirErrors.PROPERTY_AS_OPERATOR, diagnostic.symbol, context)
val unwrapped = receiver?.unwrapSmartcastExpression() ?: return
val nonFatalDiagnostics = when (unwrapped) {
is FirQualifiedAccessExpression -> unwrapped.nonFatalDiagnostics
is FirResolvedQualifier -> unwrapped.nonFatalDiagnostics
else -> return
}
val diagnosticSymbol = nonFatalDiagnostics.firstIsInstanceOrNull<ConeNotFunctionAsOperator>()?.symbol ?: return
when {
unwrapped.resolvedType.classId!!.shortClassName == OperatorNameConventions.ITERATOR -> {
reporter.reportOn(unwrapped.source, FirErrors.ITERATOR_MISSING, context)
}
else -> {
// NOT_FUNCTION_AS_OPERATOR can only happen for function calls and it's reported on the receiver expression.
reporter.reportOn(
callExpression.calleeReference.source,
FirErrors.NOT_FUNCTION_AS_OPERATOR,
if (diagnosticSymbol is FirPropertySymbol) "Property" else "Object",
diagnosticSymbol,
context
)
}
}
}
}
@@ -37,7 +37,6 @@ import org.jetbrains.kotlin.fir.resolve.calls.UnsafeCall
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.util.OperatorNameConventions
object FirForLoopChecker : FirBlockChecker(MppCheckerKind.Common) {
@@ -451,6 +451,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_FUNCTION_LA
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_LOOP_LABEL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_MULTIPLATFORM_COMPILATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_SUPERTYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_FUNCTION_AS_OPERATOR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_NULL_ASSERTION_ON_CALLABLE_REFERENCE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_SUPPORTED_INLINE_PARAMETER_IN_INLINE_PARAMETER_DEFAULT_VALUE
@@ -521,7 +522,6 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIVATE_SETTER_FO
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIVATE_SETTER_FOR_OPEN_PROPERTY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_AS_OPERATOR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_FIELD_DECLARATION_MISSING_INITIALIZER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_INITIALIZER_IN_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_INITIALIZER_NO_BACKING_FIELD
@@ -2410,8 +2410,9 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
TO_STRING
)
map.put(
PROPERTY_AS_OPERATOR,
"Property ''{0}'' cannot be used as operator.",
NOT_FUNCTION_AS_OPERATOR,
"{0} ''{1}'' cannot be used as operator.",
STRING,
SYMBOL
)
map.put(