[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
@@ -4529,9 +4529,10 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.PROPERTY_AS_OPERATOR) { firDiagnostic ->
PropertyAsOperatorImpl(
firSymbolBuilder.variableLikeBuilder.buildVariableSymbol(firDiagnostic.a),
add(FirErrors.NOT_FUNCTION_AS_OPERATOR) { firDiagnostic ->
NotFunctionAsOperatorImpl(
firDiagnostic.a,
firSymbolBuilder.buildSymbol(firDiagnostic.b),
firDiagnostic as KtPsiDiagnostic,
token,
)
@@ -3156,9 +3156,10 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
val operator: String
}
interface PropertyAsOperator : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = PropertyAsOperator::class
val property: KtVariableSymbol
interface NotFunctionAsOperator : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = NotFunctionAsOperator::class
val elementName: String
val elementSymbol: KtSymbol
}
interface DslScopeViolation : KtFirDiagnostic<PsiElement> {
@@ -3800,11 +3800,12 @@ internal class AssignmentOperatorShouldReturnUnitImpl(
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtExpression>(firDiagnostic, token), KtFirDiagnostic.AssignmentOperatorShouldReturnUnit
internal class PropertyAsOperatorImpl(
override val property: KtVariableSymbol,
internal class NotFunctionAsOperatorImpl(
override val elementName: String,
override val elementSymbol: KtSymbol,
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.PropertyAsOperator
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.NotFunctionAsOperator
internal class DslScopeViolationImpl(
override val calleeSymbol: KtSymbol,
@@ -1023,6 +1023,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
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() {
@@ -1023,6 +1023,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
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() {
@@ -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() {
@@ -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() {
@@ -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(
@@ -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)
}
@@ -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)
@@ -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 {
@@ -89,10 +89,10 @@ enum class CandidateApplicability {
RESOLVED_LOW_PRIORITY,
/**
* Candidate is successful but uses property of functional type as an operator.
* Candidate is successful but uses property or object of functional type as an operator.
* Tower resolve proceeds to next levels.
*/
K2_PROPERTY_AS_OPERATOR,
K2_NOT_FUNCTION_AS_OPERATOR,
/**
* Candidate is successful but uses new features that change resolve.
@@ -0,0 +1,130 @@
// ISSUE: KT-65881, KT-65760
class I: Iterator<Int> {
override fun hasNext(): Boolean = false
override fun next(): Int = 0
}
object A {
object plusAssign {
operator fun invoke(x: Int): A = A
}
object minusAssign {
operator fun invoke(x: Int): A = A
}
object timesAssign {
operator fun invoke(x: Int): A = A
}
object divAssign {
operator fun invoke(x: Int): A = A
}
object remAssign {
operator fun invoke(x: Int): A = A
}
object unaryPlus {
operator fun invoke(): A = A
}
object unaryMinus {
operator fun invoke(): A = A
}
object not {
operator fun invoke(): A = A
}
object plus {
operator fun invoke(x: Int): A = A
}
object minus {
operator fun invoke(x: Int): A = A
}
object times {
operator fun invoke(x: Int): A = A
}
object div {
operator fun invoke(x: Int): A = A
}
object rem {
operator fun invoke(x: Int): A = A
}
object get {
operator fun invoke(x: Int): A = A
}
object set {
operator fun invoke(x: Int, y: Int): A = A
}
object contains {
operator fun invoke(x: Int): A = A
}
object invoke {
operator fun invoke(): A = A
}
object rangeTo {
operator fun invoke(x: Int): A = A
}
object rangeUntil {
operator fun invoke(x: Int): A = A
}
object compareTo {
operator fun invoke(x: Int): A = A
}
object iterator {
operator fun invoke(): I = I()
}
object component1 {
operator fun invoke(): A = A
}
object component2 {
operator fun invoke(): A = A
}
}
fun usePlusAssign() { A <!NOT_FUNCTION_AS_OPERATOR!>+=<!> 1 }
fun useMinusAssign() { A <!NOT_FUNCTION_AS_OPERATOR!>-=<!> 1 }
fun useTimesAssign() { A <!NOT_FUNCTION_AS_OPERATOR!>*=<!> 1 }
fun useDivAssign() { A <!NOT_FUNCTION_AS_OPERATOR!>/=<!> 1 }
fun useRemAssign() { A <!NOT_FUNCTION_AS_OPERATOR!>%=<!> 1 }
val useUnaryPlus = <!NOT_FUNCTION_AS_OPERATOR!>+<!>A
val useUnaryMinus = <!NOT_FUNCTION_AS_OPERATOR!>-<!>A
val useNot = <!NOT_FUNCTION_AS_OPERATOR!>!<!>A
val usePlus = A <!NOT_FUNCTION_AS_OPERATOR!>+<!> 1
val useMinus = A <!NOT_FUNCTION_AS_OPERATOR!>-<!> 1
val useTimes = A <!NOT_FUNCTION_AS_OPERATOR!>*<!> 1
val useDiv = A <!NOT_FUNCTION_AS_OPERATOR!>/<!> 1
val useRem = A <!NOT_FUNCTION_AS_OPERATOR!>%<!> 1
val useGet = <!NOT_FUNCTION_AS_OPERATOR!>A[1]<!>
fun useSet() { <!NOT_FUNCTION_AS_OPERATOR!>A[1]<!> = 3 }
val useContains = 1 <!NOT_FUNCTION_AS_OPERATOR!>in<!> A
val useNotContains = 1 <!NOT_FUNCTION_AS_OPERATOR, UNRESOLVED_REFERENCE!>!in<!> A
val useInvoke = <!UNRESOLVED_REFERENCE!>A<!>()
val useRangeTo = A <!NOT_FUNCTION_AS_OPERATOR!>..<!> 3
val useRangeUntil = A <!NOT_FUNCTION_AS_OPERATOR!>..<<!> 3
val useCompareTo = A <!NOT_FUNCTION_AS_OPERATOR!>><!> 2
fun useIterator() {
for (x in <!ITERATOR_MISSING!>A<!>) { }
}
fun useComponentN() {
val (x, y) = <!COMPONENT_FUNCTION_MISSING, COMPONENT_FUNCTION_MISSING!>A<!>
}
object D {
object getValue {
operator fun invoke(thisRef: Any?, property: Any?): Int = 0
}
object setValue {
operator fun invoke(thisRef: Any?, property: Any?, newValue: Int) { }
}
}
class X {
val component1 = { "UwU" }
}
operator fun X.component1(): String = "Not UwU"
fun useDelegate() {
val m by <!DELEGATE_SPECIAL_FUNCTION_MISSING!>D<!>
var n by <!DELEGATE_SPECIAL_FUNCTION_MISSING, DELEGATE_SPECIAL_FUNCTION_MISSING!>D<!>
}
fun resolveToExtension() {
val (uwu) = X() // KT-65760
}
@@ -0,0 +1,130 @@
// ISSUE: KT-65881, KT-65760
class I: Iterator<Int> {
override fun hasNext(): Boolean = false
override fun next(): Int = 0
}
object A {
object plusAssign {
operator fun invoke(x: Int): A = A
}
object minusAssign {
operator fun invoke(x: Int): A = A
}
object timesAssign {
operator fun invoke(x: Int): A = A
}
object divAssign {
operator fun invoke(x: Int): A = A
}
object remAssign {
operator fun invoke(x: Int): A = A
}
object unaryPlus {
operator fun invoke(): A = A
}
object unaryMinus {
operator fun invoke(): A = A
}
object not {
operator fun invoke(): A = A
}
object plus {
operator fun invoke(x: Int): A = A
}
object minus {
operator fun invoke(x: Int): A = A
}
object times {
operator fun invoke(x: Int): A = A
}
object div {
operator fun invoke(x: Int): A = A
}
object rem {
operator fun invoke(x: Int): A = A
}
object get {
operator fun invoke(x: Int): A = A
}
object set {
operator fun invoke(x: Int, y: Int): A = A
}
object contains {
operator fun invoke(x: Int): A = A
}
object invoke {
operator fun invoke(): A = A
}
object rangeTo {
operator fun invoke(x: Int): A = A
}
object rangeUntil {
operator fun invoke(x: Int): A = A
}
object compareTo {
operator fun invoke(x: Int): A = A
}
object iterator {
operator fun invoke(): I = I()
}
object component1 {
operator fun invoke(): A = A
}
object component2 {
operator fun invoke(): A = A
}
}
fun usePlusAssign() { A <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>+=<!> 1 }
fun useMinusAssign() { A <!RESOLUTION_TO_CLASSIFIER!>-=<!> 1 }
fun useTimesAssign() { A <!RESOLUTION_TO_CLASSIFIER!>*=<!> 1 }
fun useDivAssign() { A <!RESOLUTION_TO_CLASSIFIER!>/=<!> 1 }
fun useRemAssign() { A <!RESOLUTION_TO_CLASSIFIER!>%=<!> 1 }
val useUnaryPlus = <!RESOLUTION_TO_CLASSIFIER!>+<!>A
val useUnaryMinus = <!RESOLUTION_TO_CLASSIFIER!>-<!>A
val useNot = <!RESOLUTION_TO_CLASSIFIER!>!<!>A
val usePlus = A <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>+<!> 1
val useMinus = A <!RESOLUTION_TO_CLASSIFIER!>-<!> 1
val useTimes = A <!RESOLUTION_TO_CLASSIFIER!>*<!> 1
val useDiv = A <!RESOLUTION_TO_CLASSIFIER!>/<!> 1
val useRem = A <!RESOLUTION_TO_CLASSIFIER!>%<!> 1
val useGet = <!RESOLUTION_TO_CLASSIFIER!>A<!NO_GET_METHOD!>[1]<!><!>
fun useSet() { <!RESOLUTION_TO_CLASSIFIER!>A<!NO_SET_METHOD!>[1]<!><!> = 3 }
val useContains = 1 <!RESOLUTION_TO_CLASSIFIER!>in<!> A
val useNotContains = 1 <!RESOLUTION_TO_CLASSIFIER!>!in<!> A
val useInvoke = <!FUNCTION_EXPECTED!>A<!>()
val useRangeTo = A <!RESOLUTION_TO_CLASSIFIER!>..<!> 3
val useRangeUntil = A <!RESOLUTION_TO_CLASSIFIER!>..<<!> 3
val useCompareTo = A <!RESOLUTION_TO_CLASSIFIER!>><!> 2
fun useIterator() {
for (x in <!ITERATOR_MISSING!>A<!>) { }
}
fun useComponentN() {
val (x, y) = <!COMPONENT_FUNCTION_MISSING, COMPONENT_FUNCTION_MISSING!>A<!>
}
object D {
object getValue {
operator fun invoke(thisRef: Any?, property: Any?): Int = 0
}
object setValue {
operator fun invoke(thisRef: Any?, property: Any?, newValue: Int) { }
}
}
class X {
val component1 = { "UwU" }
}
operator fun X.<!EXTENSION_FUNCTION_SHADOWED_BY_MEMBER_PROPERTY_WITH_INVOKE!>component1<!>(): String = "Not UwU"
fun useDelegate() {
val m by D
var n by <!DELEGATE_SPECIAL_FUNCTION_MISSING!>D<!>
}
fun resolveToExtension() {
val (<!PROPERTY_AS_OPERATOR!>uwu<!>) = X() // KT-65760
}
@@ -12,11 +12,11 @@ val <X3> C<X3>.getValue: D<X3> get() = TODO()
operator fun <X4> D<X4>.invoke(x: Any?, y: Any?): X4 = TODO()
fun foo(a: A<String>, c: C<String>) {
val x1 by <!PROPERTY_AS_OPERATOR!>a<!>
val x1 by <!NOT_FUNCTION_AS_OPERATOR!>a<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>x1<!>
x1.length
val y1 by <!PROPERTY_AS_OPERATOR!>c<!>
val y1 by <!NOT_FUNCTION_AS_OPERATOR!>c<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>y1<!>
y1.length
}
+6
View File
@@ -0,0 +1,6 @@
val Int.plusAssign: (Int) -> Unit
get() = {}
fun main() {
1 <!NOT_FUNCTION_AS_OPERATOR!>+=<!> 2
}
-2
View File
@@ -1,5 +1,3 @@
// FIR_IDENTICAL
val Int.plusAssign: (Int) -> Unit
get() = {}
@@ -10,25 +10,25 @@ class A { }
var a = A()
val A.inc: I get() = I()
fun useInc() { a<!PROPERTY_AS_OPERATOR, RESULT_TYPE_MISMATCH!>++<!> }
fun useInc() { a<!NOT_FUNCTION_AS_OPERATOR, RESULT_TYPE_MISMATCH!>++<!> }
val A.dec: I get() = I()
fun useDec() { a<!PROPERTY_AS_OPERATOR, RESULT_TYPE_MISMATCH!>--<!> }
fun useDec() { a<!NOT_FUNCTION_AS_OPERATOR, RESULT_TYPE_MISMATCH!>--<!> }
val A.plusAssign: I get() = I()
fun usePlusAssign() { a <!PROPERTY_AS_OPERATOR!>+=<!> 1 }
fun usePlusAssign() { a <!NOT_FUNCTION_AS_OPERATOR!>+=<!> 1 }
val A.minusAssign: I get() = I()
fun useMinusAssign() { a <!PROPERTY_AS_OPERATOR!>-=<!> 1 }
fun useMinusAssign() { a <!NOT_FUNCTION_AS_OPERATOR!>-=<!> 1 }
val A.timesAssign: I get() = I()
fun useTimesAssign() { a <!PROPERTY_AS_OPERATOR!>*=<!> 1 }
fun useTimesAssign() { a <!NOT_FUNCTION_AS_OPERATOR!>*=<!> 1 }
val A.divAssign: I get() = I()
fun useDivAssign() { a <!PROPERTY_AS_OPERATOR!>/=<!> 1 }
fun useDivAssign() { a <!NOT_FUNCTION_AS_OPERATOR!>/=<!> 1 }
val A.remAssign: I get() = I()
fun useRemAssign() { a <!PROPERTY_AS_OPERATOR!>%=<!> 1 }
fun useRemAssign() { a <!NOT_FUNCTION_AS_OPERATOR!>%=<!> 1 }
// operators over values
@@ -36,47 +36,47 @@ class E { }
val e = E()
val E.unaryPlus: I get() = I()
val useUnaryPlus = <!PROPERTY_AS_OPERATOR!>+<!>e
val useUnaryPlus = <!NOT_FUNCTION_AS_OPERATOR!>+<!>e
val E.unaryMinus: I get() = I()
val useUnaryMinus = <!PROPERTY_AS_OPERATOR!>-<!>e
val useUnaryMinus = <!NOT_FUNCTION_AS_OPERATOR!>-<!>e
val E.not: I get() = I()
val useNot = <!PROPERTY_AS_OPERATOR!>!<!>e
val useNot = <!NOT_FUNCTION_AS_OPERATOR!>!<!>e
val E.plus: I get() = I()
val usePlus = e <!PROPERTY_AS_OPERATOR!>+<!> 1
val usePlus = e <!NOT_FUNCTION_AS_OPERATOR!>+<!> 1
val E.minus: I get() = I()
val useMinus = e <!PROPERTY_AS_OPERATOR!>-<!> 1
val useMinus = e <!NOT_FUNCTION_AS_OPERATOR!>-<!> 1
val E.times: I get() = I()
val useTimes = e <!PROPERTY_AS_OPERATOR!>*<!> 1
val useTimes = e <!NOT_FUNCTION_AS_OPERATOR!>*<!> 1
val E.div: I get() = I()
val useDiv = e <!PROPERTY_AS_OPERATOR!>/<!> 1
val useDiv = e <!NOT_FUNCTION_AS_OPERATOR!>/<!> 1
val E.rem: I get() = I()
val useRem = e <!PROPERTY_AS_OPERATOR!>%<!> 1
val useRem = e <!NOT_FUNCTION_AS_OPERATOR!>%<!> 1
val E.get: I get() = I()
val useGet = <!PROPERTY_AS_OPERATOR!>e[1]<!>
val useGet = <!NOT_FUNCTION_AS_OPERATOR!>e[1]<!>
val E.set: I get() = I()
fun useSet() { <!PROPERTY_AS_OPERATOR!>e[1]<!> = 3 }
fun useSet() { <!NOT_FUNCTION_AS_OPERATOR!>e[1]<!> = 3 }
val E.contains: I get() = I()
val useContains = 1 <!PROPERTY_AS_OPERATOR!>in<!> e
val useNotContains = 1 <!PROPERTY_AS_OPERATOR, PROPERTY_AS_OPERATOR!>!in<!> e
val useContains = 1 <!NOT_FUNCTION_AS_OPERATOR!>in<!> e
val useNotContains = 1 <!NOT_FUNCTION_AS_OPERATOR, NOT_FUNCTION_AS_OPERATOR!>!in<!> e
val E.invoke: I get() = I()
val useInvoke = <!NONE_APPLICABLE!>e<!>()
val E.rangeTo: I get() = I()
val useRangeTo = e <!PROPERTY_AS_OPERATOR!>..<!> 3
val useRangeTo = e <!NOT_FUNCTION_AS_OPERATOR!>..<!> 3
val E.rangeUntil: I get() = I()
val useRangeUntil = e <!PROPERTY_AS_OPERATOR!>..<<!> 3
val useRangeUntil = e <!NOT_FUNCTION_AS_OPERATOR!>..<<!> 3
val E.compareTo: I get() = I()
val useCompareTo = e <!PROPERTY_AS_OPERATOR!>><!> 2
val useCompareTo = e <!NOT_FUNCTION_AS_OPERATOR!>><!> 2
@@ -0,0 +1,23 @@
// ISSUE: KT-59715
class FunctionComponent {
val component1: () -> String = { "hello" }
}
class I {
operator fun invoke(): String = "hello"
}
class InvokeComponent {
val component1: I = I()
}
fun test_1(c: FunctionComponent) {
val (<!NOT_FUNCTION_AS_OPERATOR!>x<!>) = FunctionComponent()
val (<!NOT_FUNCTION_AS_OPERATOR!>y<!>) = c
}
fun test_2(c: InvokeComponent) {
val (<!NOT_FUNCTION_AS_OPERATOR!>x<!>) = FunctionComponent()
val (<!NOT_FUNCTION_AS_OPERATOR!>y<!>) = c
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// ISSUE: KT-59715
class FunctionComponent {
@@ -1023,6 +1023,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
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() {
@@ -25,7 +25,7 @@ fun case1() {
}
class B() {
val p: String by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, PROPERTY_AS_OPERATOR!>Delegate()<!> // DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE expected
val p: String by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, NOT_FUNCTION_AS_OPERATOR!>Delegate()<!> // DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE expected
}
class Delegate {
@@ -50,7 +50,7 @@ fun case2() {
}
class B() {
var p: String by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, PROPERTY_AS_OPERATOR, PROPERTY_AS_OPERATOR!>Delegate()<!> // DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE expected
var p: String by <!DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, NOT_FUNCTION_AS_OPERATOR, NOT_FUNCTION_AS_OPERATOR!>Delegate()<!> // DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE expected
}
class Delegate {
@@ -109,14 +109,14 @@ class B() {
fun case3() {
var b = B()
b <!PROPERTY_AS_OPERATOR!>+<!> 5
b <!PROPERTY_AS_OPERATOR!>-<!> 5
b <!PROPERTY_AS_OPERATOR!><<!> 5
b <!PROPERTY_AS_OPERATOR!>>=<!> 5
1 <!PROPERTY_AS_OPERATOR!>in<!> b
<!PROPERTY_AS_OPERATOR!>b[2]<!>
<!PROPERTY_AS_OPERATOR!>b[3]<!> = 4
<!PROPERTY_AS_OPERATOR!>+<!>b
b <!NOT_FUNCTION_AS_OPERATOR!>+<!> 5
b <!NOT_FUNCTION_AS_OPERATOR!>-<!> 5
b <!NOT_FUNCTION_AS_OPERATOR!><<!> 5
b <!NOT_FUNCTION_AS_OPERATOR!>>=<!> 5
1 <!NOT_FUNCTION_AS_OPERATOR!>in<!> b
<!NOT_FUNCTION_AS_OPERATOR!>b[2]<!>
<!NOT_FUNCTION_AS_OPERATOR!>b[3]<!> = 4
<!NOT_FUNCTION_AS_OPERATOR!>+<!>b
}
// FILE: TestCase4.kt
@@ -135,8 +135,8 @@ class B(val minusAssign: Assign = Assign()) {
fun case3() {
var b = B()
b <!PROPERTY_AS_OPERATOR!>+=<!> 2
b <!PROPERTY_AS_OPERATOR!>-=<!> 3
b <!NOT_FUNCTION_AS_OPERATOR!>+=<!> 2
b <!NOT_FUNCTION_AS_OPERATOR!>-=<!> 3
}
// FILE: TestCase5.kt
@@ -175,7 +175,7 @@ class B(var a: Int = 0) {
val plus :E =TODO()
fun foo(b: B){
this <!PROPERTY_AS_OPERATOR!>+<!> 1
this <!NOT_FUNCTION_AS_OPERATOR!>+<!> 1
}
operator fun invoke(value: Int) = B()
@@ -193,7 +193,7 @@ package testPackCase7
fun case7 () {
val iterable: Iterable = Iterable(Inv('s'))
for (i in <!PROPERTY_AS_OPERATOR!>iterable<!>) {
for (i in <!NOT_FUNCTION_AS_OPERATOR!>iterable<!>) {
println(i)
}
}
@@ -226,7 +226,7 @@ package testPackCase8
fun case8 () {
val iterable: Iterable = Iterable()
for (i in <!PROPERTY_AS_OPERATOR!>iterable<!>) {
for (i in <!NOT_FUNCTION_AS_OPERATOR!>iterable<!>) {
println(i)
}
}
@@ -29,7 +29,7 @@ class B() {
fun case7(){
var b =B()
b<!ASSIGN_OPERATOR_AMBIGUITY!>+=<!>1 //ASSIGN_OPERATOR_AMBIGUITY
this <!PROPERTY_AS_OPERATOR!>+=<!> 1 //ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT, PROPERTY_AS_OPERATOR
this <!NOT_FUNCTION_AS_OPERATOR!>+=<!> 1 //ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT, PROPERTY_AS_OPERATOR
}
}
@@ -25,7 +25,7 @@ class Case1() {
fun foo(e: E) {
if (e.plus != null) {
run { e <!PROPERTY_AS_OPERATOR!>+<!> 1 }
run { e <!NOT_FUNCTION_AS_OPERATOR!>+<!> 1 }
/*
[PROPERTY_AS_OPERATOR] (ok)
@@ -33,7 +33,7 @@ class Case1() {
[UNSAFE_OPERATOR_CALL] (nok)
Operator call corresponds to a dot-qualified call 'e.plus(1)' which is not allowed on a nullable receiver 'e'.
*/
e <!PROPERTY_AS_OPERATOR!>+<!> 1
e <!NOT_FUNCTION_AS_OPERATOR!>+<!> 1
e.plus.invoke(1) //ok
}