[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:
committed by
Space Team
parent
df9d59851d
commit
2d4f4b9bb5
+6
@@ -1023,6 +1023,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
||||
runTest("compiler/testData/diagnostics/tests/OperatorChecks.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("OperatorConventions.kt")
|
||||
public void testOperatorConventions() {
|
||||
runTest("compiler/testData/diagnostics/tests/OperatorConventions.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("Operators.kt")
|
||||
public void testOperators() {
|
||||
|
||||
+6
@@ -1023,6 +1023,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
||||
runTest("compiler/testData/diagnostics/tests/OperatorChecks.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("OperatorConventions.kt")
|
||||
public void testOperatorConventions() {
|
||||
runTest("compiler/testData/diagnostics/tests/OperatorConventions.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("Operators.kt")
|
||||
public void testOperators() {
|
||||
|
||||
+3
-2
@@ -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")
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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",
|
||||
|
||||
+29
-13
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -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) {
|
||||
|
||||
+4
-3
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
+16
-8
@@ -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()
|
||||
|
||||
|
||||
+3
-3
@@ -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)
|
||||
}
|
||||
|
||||
+1
-2
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.ConeTypeVariable
|
||||
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
|
||||
@@ -136,7 +135,7 @@ class OperatorCallOfConstructor(val constructor: FirConstructorSymbol) : Resolut
|
||||
class InferenceError(val constraintError: ConstraintSystemError) : ResolutionDiagnostic(constraintError.applicability)
|
||||
class Unsupported(val message: String, val source: KtSourceElement?) : ResolutionDiagnostic(K2_UNSUPPORTED)
|
||||
|
||||
class PropertyAsOperator(val propertySymbol: FirPropertySymbol) : ResolutionDiagnostic(K2_PROPERTY_AS_OPERATOR)
|
||||
class NotFunctionAsOperator(val symbol: FirBasedSymbol<*>) : ResolutionDiagnostic(K2_NOT_FUNCTION_AS_OPERATOR)
|
||||
|
||||
class DslScopeViolation(val calleeSymbol: FirBasedSymbol<*>) : ResolutionDiagnostic(RESOLVED_WITH_ERROR)
|
||||
|
||||
|
||||
+2
-2
@@ -350,8 +350,8 @@ class ConeLocalVariableNoTypeOrInitializer(val variable: FirVariable) : ConeDiag
|
||||
override val reason: String get() = "Cannot infer variable type without initializer / getter / delegate"
|
||||
}
|
||||
|
||||
class ConePropertyAsOperator(val symbol: FirPropertySymbol) : ConeDiagnostic {
|
||||
override val reason: String get() = "Cannot use a property as an operator"
|
||||
class ConeNotFunctionAsOperator(val symbol: FirBasedSymbol<*>) : ConeDiagnostic {
|
||||
override val reason: String get() = "Cannot use not function as an operator"
|
||||
}
|
||||
|
||||
class ConeUnknownLambdaParameterTypeDiagnostic : ConeDiagnostic {
|
||||
|
||||
Reference in New Issue
Block a user