FIR checker: report PROPERTY_AS_OPERATOR
This commit is contained in:
committed by
Mikhail Glukhikh
parent
4915d8dda3
commit
05fbed68e2
+3
@@ -1145,6 +1145,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
|||||||
parameter<FirNamedFunctionSymbol>("functionSymbol")
|
parameter<FirNamedFunctionSymbol>("functionSymbol")
|
||||||
parameter<String>("operator")
|
parameter<String>("operator")
|
||||||
}
|
}
|
||||||
|
val PROPERTY_AS_OPERATOR by error<PsiElement>(PositioningStrategy.OPERATOR) {
|
||||||
|
parameter<FirPropertySymbol>("property")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val TYPE_ALIAS by object : DiagnosticGroup("Type alias") {
|
val TYPE_ALIAS by object : DiagnosticGroup("Type alias") {
|
||||||
|
|||||||
@@ -596,6 +596,7 @@ object FirErrors {
|
|||||||
val INCOMPATIBLE_ENUM_COMPARISON_ERROR by error2<KtElement, ConeKotlinType, ConeKotlinType>()
|
val INCOMPATIBLE_ENUM_COMPARISON_ERROR by error2<KtElement, ConeKotlinType, ConeKotlinType>()
|
||||||
val INC_DEC_SHOULD_NOT_RETURN_UNIT by error0<KtExpression>(SourceElementPositioningStrategies.OPERATOR)
|
val INC_DEC_SHOULD_NOT_RETURN_UNIT by error0<KtExpression>(SourceElementPositioningStrategies.OPERATOR)
|
||||||
val ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT by error2<KtExpression, FirNamedFunctionSymbol, String>(SourceElementPositioningStrategies.OPERATOR)
|
val ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT by error2<KtExpression, FirNamedFunctionSymbol, String>(SourceElementPositioningStrategies.OPERATOR)
|
||||||
|
val PROPERTY_AS_OPERATOR by error1<PsiElement, FirPropertySymbol>(SourceElementPositioningStrategies.OPERATOR)
|
||||||
|
|
||||||
// Type alias
|
// Type alias
|
||||||
val TOPLEVEL_TYPEALIASES_ONLY by error0<KtTypeAlias>()
|
val TOPLEVEL_TYPEALIASES_ONLY by error0<KtTypeAlias>()
|
||||||
|
|||||||
+29
@@ -6,17 +6,26 @@
|
|||||||
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||||
|
|
||||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||||
|
import org.jetbrains.kotlin.fir.FirRealSourceElementKind
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
|
||||||
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConePropertyAsOperator
|
||||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
|
|
||||||
object FirConventionFunctionCallChecker : FirFunctionCallChecker() {
|
object FirConventionFunctionCallChecker : FirFunctionCallChecker() {
|
||||||
override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
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)
|
||||||
val calleeReference = expression.calleeReference as? FirErrorNamedReference ?: return
|
val calleeReference = expression.calleeReference as? FirErrorNamedReference ?: return
|
||||||
val diagnostic = calleeReference.diagnostic as? ConeUnresolvedNameError ?: return
|
val diagnostic = calleeReference.diagnostic as? ConeUnresolvedNameError ?: return
|
||||||
|
|
||||||
@@ -27,4 +36,24 @@ object FirConventionFunctionCallChecker : FirFunctionCallChecker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkPropertyAsOperator(
|
||||||
|
callExpression: FirFunctionCall,
|
||||||
|
receiver: FirExpression,
|
||||||
|
context: CheckerContext,
|
||||||
|
reporter: DiagnosticReporter
|
||||||
|
) {
|
||||||
|
val sourceKind = callExpression.source?.kind
|
||||||
|
if (sourceKind !is FirRealSourceElementKind &&
|
||||||
|
sourceKind !is FirFakeSourceElementKind.GeneratedComparisonExpression &&
|
||||||
|
sourceKind !is FirFakeSourceElementKind.DesugaredCompoundAssignment
|
||||||
|
) return
|
||||||
|
val unwrapped = when (receiver) {
|
||||||
|
is FirExpressionWithSmartcast -> receiver.originalExpression
|
||||||
|
else -> receiver
|
||||||
|
}
|
||||||
|
if (unwrapped !is FirPropertyAccessExpression) return
|
||||||
|
val diagnostic = unwrapped.nonFatalDiagnostics.firstIsInstanceOrNull<ConePropertyAsOperator>() ?: return
|
||||||
|
reporter.reportOn(callExpression.calleeReference.source, FirErrors.PROPERTY_AS_OPERATOR, diagnostic.symbol, context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -332,6 +332,7 @@ 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.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_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.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_INITIALIZER_IN_INTERFACE
|
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
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_INITIALIZER_NO_BACKING_FIELD
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_TYPE_MISMATCH_BY_DELEGATION
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_TYPE_MISMATCH_BY_DELEGATION
|
||||||
@@ -1515,6 +1516,11 @@ class FirDefaultErrorMessages {
|
|||||||
SYMBOL,
|
SYMBOL,
|
||||||
TO_STRING
|
TO_STRING
|
||||||
)
|
)
|
||||||
|
map.put(
|
||||||
|
PROPERTY_AS_OPERATOR,
|
||||||
|
"Property ''{0}'' cannot be used as an operator.",
|
||||||
|
SYMBOL
|
||||||
|
)
|
||||||
|
|
||||||
// Type alias
|
// Type alias
|
||||||
map.put(TOPLEVEL_TYPEALIASES_ONLY, "Nested and local type aliases are not supported")
|
map.put(TOPLEVEL_TYPEALIASES_ONLY, "Nested and local type aliases are not supported")
|
||||||
|
|||||||
@@ -84,11 +84,11 @@ class FirCallResolver(
|
|||||||
|
|
||||||
return components.context.withIncrementedQualifierPartIndex {
|
return components.context.withIncrementedQualifierPartIndex {
|
||||||
val name = functionCall.calleeReference.name
|
val name = functionCall.calleeReference.name
|
||||||
val result = collectCandidates(functionCall, name)
|
val result = collectCandidates(functionCall, name, origin = functionCall.origin)
|
||||||
|
|
||||||
var forceCandidates: Collection<Candidate>? = null
|
var forceCandidates: Collection<Candidate>? = null
|
||||||
if (result.candidates.isEmpty()) {
|
if (result.candidates.isEmpty()) {
|
||||||
val newResult = collectCandidates(functionCall, name, CallKind.VariableAccess)
|
val newResult = collectCandidates(functionCall, name, CallKind.VariableAccess, origin = functionCall.origin)
|
||||||
if (newResult.candidates.isNotEmpty()) {
|
if (newResult.candidates.isNotEmpty()) {
|
||||||
forceCandidates = newResult.candidates
|
forceCandidates = newResult.candidates
|
||||||
}
|
}
|
||||||
@@ -152,7 +152,8 @@ class FirCallResolver(
|
|||||||
private fun <T : FirQualifiedAccess> collectCandidates(
|
private fun <T : FirQualifiedAccess> collectCandidates(
|
||||||
qualifiedAccess: T,
|
qualifiedAccess: T,
|
||||||
name: Name,
|
name: Name,
|
||||||
forceCallKind: CallKind? = null
|
forceCallKind: CallKind? = null,
|
||||||
|
origin: FirFunctionCallOrigin = FirFunctionCallOrigin.Regular
|
||||||
): ResolutionResult {
|
): ResolutionResult {
|
||||||
val explicitReceiver = qualifiedAccess.explicitReceiver
|
val explicitReceiver = qualifiedAccess.explicitReceiver
|
||||||
val argumentList = (qualifiedAccess as? FirFunctionCall)?.argumentList ?: FirEmptyArgumentList
|
val argumentList = (qualifiedAccess as? FirFunctionCall)?.argumentList ?: FirEmptyArgumentList
|
||||||
@@ -172,6 +173,7 @@ class FirCallResolver(
|
|||||||
session,
|
session,
|
||||||
components.file,
|
components.file,
|
||||||
transformer.components.containingDeclarations,
|
transformer.components.containingDeclarations,
|
||||||
|
origin = origin
|
||||||
)
|
)
|
||||||
towerResolver.reset()
|
towerResolver.reset()
|
||||||
val result = towerResolver.runResolver(info, transformer.resolutionContext)
|
val result = towerResolver.runResolver(info, transformer.resolutionContext)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
|||||||
import org.jetbrains.kotlin.fir.expressions.FirArgumentList
|
import org.jetbrains.kotlin.fir.expressions.FirArgumentList
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirEmptyArgumentList
|
import org.jetbrains.kotlin.fir.expressions.FirEmptyArgumentList
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirFunctionCallOrigin
|
||||||
import org.jetbrains.kotlin.fir.expressions.builder.buildArgumentList
|
import org.jetbrains.kotlin.fir.expressions.builder.buildArgumentList
|
||||||
import org.jetbrains.kotlin.fir.expressions.impl.FirExpressionStub
|
import org.jetbrains.kotlin.fir.expressions.impl.FirExpressionStub
|
||||||
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
|
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
|
||||||
@@ -55,7 +56,8 @@ data class CallInfo(
|
|||||||
// Four properties for callable references only
|
// Four properties for callable references only
|
||||||
val expectedType: ConeKotlinType? = null,
|
val expectedType: ConeKotlinType? = null,
|
||||||
val outerCSBuilder: ConstraintSystemBuilder? = null,
|
val outerCSBuilder: ConstraintSystemBuilder? = null,
|
||||||
val lhs: DoubleColonLHS? = null
|
val lhs: DoubleColonLHS? = null,
|
||||||
|
val origin: FirFunctionCallOrigin = FirFunctionCallOrigin.Regular,
|
||||||
) {
|
) {
|
||||||
val arguments: List<FirExpression> get() = argumentList.arguments
|
val arguments: List<FirExpression> get() = argumentList.arguments
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ class CandidateFactory private constructor(
|
|||||||
) {
|
) {
|
||||||
result.addDiagnostic(NoCompanionObject)
|
result.addDiagnostic(NoCompanionObject)
|
||||||
}
|
}
|
||||||
|
if (callInfo.origin == FirFunctionCallOrigin.Operator && symbol is FirPropertySymbol) {
|
||||||
|
// Flag all property references that are resolved from an convention operator call.
|
||||||
|
result.addDiagnostic(PropertyAsOperator)
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -98,4 +98,6 @@ class ManyLambdaExpressionArguments(
|
|||||||
class InfixCallOfNonInfixFunction(val function: FirNamedFunctionSymbol) : ResolutionDiagnostic(INAPPLICABLE_MODIFIER)
|
class InfixCallOfNonInfixFunction(val function: FirNamedFunctionSymbol) : ResolutionDiagnostic(INAPPLICABLE_MODIFIER)
|
||||||
class OperatorCallOfNonOperatorFunction(val function: FirNamedFunctionSymbol) : ResolutionDiagnostic(INAPPLICABLE_MODIFIER)
|
class OperatorCallOfNonOperatorFunction(val function: FirNamedFunctionSymbol) : ResolutionDiagnostic(INAPPLICABLE_MODIFIER)
|
||||||
|
|
||||||
class Unsupported(val message: String, val source: FirSourceElement? = null) : ResolutionDiagnostic(UNSUPPORTED)
|
class Unsupported(val message: String, val source: FirSourceElement? = null) : ResolutionDiagnostic(UNSUPPORTED)
|
||||||
|
|
||||||
|
object PropertyAsOperator : ResolutionDiagnostic(PROPERTY_AS_OPERATOR)
|
||||||
+7
-4
@@ -11,14 +11,13 @@ import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
|
|||||||
import org.jetbrains.kotlin.fir.expressions.builder.FirPropertyAccessExpressionBuilder
|
import org.jetbrains.kotlin.fir.expressions.builder.FirPropertyAccessExpressionBuilder
|
||||||
import org.jetbrains.kotlin.fir.resolve.*
|
import org.jetbrains.kotlin.fir.resolve.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.calls.*
|
import org.jetbrains.kotlin.fir.resolve.calls.*
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConePropertyAsOperator
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||||
import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
|
import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
|
||||||
import org.jetbrains.kotlin.fir.types.isExtensionFunctionType
|
import org.jetbrains.kotlin.fir.types.isExtensionFunctionType
|
||||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
internal class FirInvokeResolveTowerExtension(
|
internal class FirInvokeResolveTowerExtension(
|
||||||
@@ -291,6 +290,10 @@ private fun BodyResolveComponents.createExplicitReceiverForInvokeByCallable(
|
|||||||
// NB: this should fix problem in DFA (KT-36014)
|
// NB: this should fix problem in DFA (KT-36014)
|
||||||
explicitReceiver = info.explicitReceiver
|
explicitReceiver = info.explicitReceiver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (candidate.currentApplicability == CandidateApplicability.PROPERTY_AS_OPERATOR) {
|
||||||
|
nonFatalDiagnostics.add(ConePropertyAsOperator(candidate.symbol as FirPropertySymbol))
|
||||||
|
}
|
||||||
}.build().let(::transformQualifiedAccessUsingSmartcastInfo)
|
}.build().let(::transformQualifiedAccessUsingSmartcastInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
@@ -206,6 +206,10 @@ class ConeLocalVariableNoTypeOrInitializer(val variable: FirVariable) : ConeDiag
|
|||||||
override val reason: String get() = "Cannot infer variable type without initializer / getter / delegate"
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
private fun describeSymbol(symbol: FirBasedSymbol<*>): String {
|
private fun describeSymbol(symbol: FirBasedSymbol<*>): String {
|
||||||
return when (symbol) {
|
return when (symbol) {
|
||||||
is FirClassLikeSymbol<*> -> symbol.classId.asString()
|
is FirClassLikeSymbol<*> -> symbol.classId.asString()
|
||||||
|
|||||||
+7
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.fir.declarations.utils.isSuspend
|
|||||||
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
||||||
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||||
import org.jetbrains.kotlin.fir.expressions.*
|
import org.jetbrains.kotlin.fir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.impl.FirPropertyAccessExpressionImpl
|
||||||
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
||||||
import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
|
import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
|
||||||
import org.jetbrains.kotlin.fir.references.builder.buildResolvedCallableReference
|
import org.jetbrains.kotlin.fir.references.builder.buildResolvedCallableReference
|
||||||
@@ -26,6 +27,7 @@ import org.jetbrains.kotlin.fir.resolve.createFunctionalType
|
|||||||
import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer
|
import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer
|
||||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeConstraintSystemHasContradiction
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeConstraintSystemHasContradiction
|
||||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConePropertyAsOperator
|
||||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeTypeParameterInQualifiedAccess
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeTypeParameterInQualifiedAccess
|
||||||
import org.jetbrains.kotlin.fir.resolve.firProvider
|
import org.jetbrains.kotlin.fir.resolve.firProvider
|
||||||
import org.jetbrains.kotlin.fir.resolve.inference.*
|
import org.jetbrains.kotlin.fir.resolve.inference.*
|
||||||
@@ -39,6 +41,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirIntegerOperatorCall
|
|||||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
|
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
|
||||||
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||||
@@ -48,6 +51,7 @@ import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
|||||||
import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
|
import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
|
||||||
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
||||||
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
|
||||||
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
|
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
|
||||||
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
|
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
@@ -126,6 +130,9 @@ class FirCallCompletionResultsWriterTransformer(
|
|||||||
)
|
)
|
||||||
.transformDispatchReceiver(StoreReceiver, subCandidate.dispatchReceiverExpression())
|
.transformDispatchReceiver(StoreReceiver, subCandidate.dispatchReceiverExpression())
|
||||||
.transformExtensionReceiver(StoreReceiver, subCandidate.extensionReceiverExpression()) as T
|
.transformExtensionReceiver(StoreReceiver, subCandidate.extensionReceiverExpression()) as T
|
||||||
|
if (result is FirPropertyAccessExpressionImpl && calleeReference.candidate.currentApplicability == CandidateApplicability.PROPERTY_AS_OPERATOR) {
|
||||||
|
result.nonFatalDiagnostics.add(ConePropertyAsOperator(calleeReference.candidate.symbol as FirPropertySymbol))
|
||||||
|
}
|
||||||
|
|
||||||
if (result is FirQualifiedAccessExpression) {
|
if (result is FirQualifiedAccessExpression) {
|
||||||
result.replaceTypeRef(typeRef)
|
result.replaceTypeRef(typeRef)
|
||||||
|
|||||||
+1
@@ -20,6 +20,7 @@ enum class CandidateApplicability {
|
|||||||
UNSTABLE_SMARTCAST, // unstable smart cast
|
UNSTABLE_SMARTCAST, // unstable smart cast
|
||||||
CONVENTION_ERROR, // missing infix, operator etc
|
CONVENTION_ERROR, // missing infix, operator etc
|
||||||
RESOLVED_LOW_PRIORITY,
|
RESOLVED_LOW_PRIORITY,
|
||||||
|
PROPERTY_AS_OPERATOR, // using property of functional type as an operator. From resolution perspective, this is considered successful.
|
||||||
RESOLVED_NEED_PRESERVE_COMPATIBILITY, // call resolved successfully, but using new features that changes resolve
|
RESOLVED_NEED_PRESERVE_COMPATIBILITY, // call resolved successfully, but using new features that changes resolve
|
||||||
RESOLVED_WITH_ERROR, // call has error, but it is still successful from resolution perspective
|
RESOLVED_WITH_ERROR, // call has error, but it is still successful from resolution perspective
|
||||||
RESOLVED, // call success or has uncompleted inference or in other words possible successful candidate
|
RESOLVED, // call success or has uncompleted inference or in other words possible successful candidate
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
// !LANGUAGE: +NewInference
|
|
||||||
|
|
||||||
val Int.plusAssign: (Int) -> Unit
|
|
||||||
get() = {}
|
|
||||||
|
|
||||||
fun main() {
|
|
||||||
1 += 2
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// !LANGUAGE: +NewInference
|
// !LANGUAGE: +NewInference
|
||||||
|
|
||||||
val Int.plusAssign: (Int) -> Unit
|
val Int.plusAssign: (Int) -> Unit
|
||||||
|
|||||||
+32
-7
@@ -73,19 +73,44 @@ class Arifm() {
|
|||||||
operator fun invoke(i: Int) {}
|
operator fun invoke(i: Int) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Contains() {
|
||||||
|
operator fun invoke(i: Int): Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
|
class Getter() {
|
||||||
|
operator fun invoke(i: Int): Int = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
class Setter() {
|
||||||
|
operator fun invoke(i: Int, j: Int) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Unary<B>() {
|
||||||
|
operator fun invoke(): B = TODO()
|
||||||
|
}
|
||||||
|
|
||||||
class B() {
|
class B() {
|
||||||
val plus = Arifm()
|
val plus = Arifm()
|
||||||
val minus = Arifm()
|
val minus = Arifm()
|
||||||
val compareTo = Comp()
|
val compareTo = Comp()
|
||||||
|
val contains = Contains()
|
||||||
|
val get = Getter()
|
||||||
|
val set = Setter()
|
||||||
|
val unaryPlus = Unary<B>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun case3() {
|
fun case3() {
|
||||||
var b = B()
|
var b = B()
|
||||||
b + 5
|
b <!PROPERTY_AS_OPERATOR!>+<!> 5
|
||||||
b - 5
|
b <!PROPERTY_AS_OPERATOR!>-<!> 5
|
||||||
b < 5
|
b <!PROPERTY_AS_OPERATOR!><<!> 5
|
||||||
b >= 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// FILE: TestCase4.kt
|
// FILE: TestCase4.kt
|
||||||
/*
|
/*
|
||||||
* TESTCASE NUMBER: 4
|
* TESTCASE NUMBER: 4
|
||||||
@@ -102,8 +127,8 @@ class B(val minusAssign: Assign = Assign()) {
|
|||||||
|
|
||||||
fun case3() {
|
fun case3() {
|
||||||
var b = B()
|
var b = B()
|
||||||
b += 2
|
b <!PROPERTY_AS_OPERATOR!>+=<!> 2
|
||||||
b -= 3
|
b <!PROPERTY_AS_OPERATOR!>-=<!> 3
|
||||||
}
|
}
|
||||||
|
|
||||||
// FILE: TestCase5.kt
|
// FILE: TestCase5.kt
|
||||||
@@ -142,7 +167,7 @@ class B(var a: Int = 0) {
|
|||||||
val plus :E =TODO()
|
val plus :E =TODO()
|
||||||
|
|
||||||
fun foo(b: B){
|
fun foo(b: B){
|
||||||
this + 1
|
this <!PROPERTY_AS_OPERATOR!>+<!> 1
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun invoke(value: Int) = B()
|
operator fun invoke(value: Int) = B()
|
||||||
|
|||||||
+25
@@ -82,10 +82,30 @@ class Arifm() {
|
|||||||
operator fun invoke(i: Int) {}
|
operator fun invoke(i: Int) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Contains() {
|
||||||
|
operator fun invoke(i: Int): Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
|
class Getter() {
|
||||||
|
operator fun invoke(i: Int): Int = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
class Setter() {
|
||||||
|
operator fun invoke(i: Int, j: Int) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Unary<B>() {
|
||||||
|
operator fun invoke(): B = TODO()
|
||||||
|
}
|
||||||
|
|
||||||
class B() {
|
class B() {
|
||||||
val plus = Arifm()
|
val plus = Arifm()
|
||||||
val minus = Arifm()
|
val minus = Arifm()
|
||||||
val compareTo = Comp()
|
val compareTo = Comp()
|
||||||
|
val contains = Contains()
|
||||||
|
val get = Getter()
|
||||||
|
val set = Setter()
|
||||||
|
val unaryPlus = Unary<B>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun case3() {
|
fun case3() {
|
||||||
@@ -94,7 +114,12 @@ fun case3() {
|
|||||||
b <!PROPERTY_AS_OPERATOR!>-<!> 5
|
b <!PROPERTY_AS_OPERATOR!>-<!> 5
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// FILE: TestCase4.kt
|
// FILE: TestCase4.kt
|
||||||
/*
|
/*
|
||||||
* TESTCASE NUMBER: 4
|
* TESTCASE NUMBER: 4
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ class B() {
|
|||||||
fun case7(){
|
fun case7(){
|
||||||
var b =B()
|
var b =B()
|
||||||
<!ASSIGN_OPERATOR_AMBIGUITY!>b+=1<!> //ASSIGN_OPERATOR_AMBIGUITY
|
<!ASSIGN_OPERATOR_AMBIGUITY!>b+=1<!> //ASSIGN_OPERATOR_AMBIGUITY
|
||||||
this += 1 //ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT, PROPERTY_AS_OPERATOR
|
this <!PROPERTY_AS_OPERATOR!>+=<!> 1 //ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT, PROPERTY_AS_OPERATOR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -14,7 +14,7 @@ class Case1() {
|
|||||||
fun foo(e: E) {
|
fun foo(e: E) {
|
||||||
|
|
||||||
if (e.plus != null) {
|
if (e.plus != null) {
|
||||||
run { e + 1 }
|
run { e <!PROPERTY_AS_OPERATOR!>+<!> 1 }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
[PROPERTY_AS_OPERATOR] (ok)
|
[PROPERTY_AS_OPERATOR] (ok)
|
||||||
@@ -22,7 +22,7 @@ class Case1() {
|
|||||||
[UNSAFE_OPERATOR_CALL] (nok)
|
[UNSAFE_OPERATOR_CALL] (nok)
|
||||||
Operator call corresponds to a dot-qualified call 'e.plus(1)' which is not allowed on a nullable receiver 'e'.
|
Operator call corresponds to a dot-qualified call 'e.plus(1)' which is not allowed on a nullable receiver 'e'.
|
||||||
*/
|
*/
|
||||||
e + 1
|
e <!PROPERTY_AS_OPERATOR!>+<!> 1
|
||||||
|
|
||||||
e.plus.invoke(1) //ok
|
e.plus.invoke(1) //ok
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -3131,6 +3131,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
add(FirErrors.PROPERTY_AS_OPERATOR) { firDiagnostic ->
|
||||||
|
PropertyAsOperatorImpl(
|
||||||
|
firSymbolBuilder.variableLikeBuilder.buildVariableSymbol(firDiagnostic.a.fir),
|
||||||
|
firDiagnostic as FirPsiDiagnostic,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
add(FirErrors.TOPLEVEL_TYPEALIASES_ONLY) { firDiagnostic ->
|
add(FirErrors.TOPLEVEL_TYPEALIASES_ONLY) { firDiagnostic ->
|
||||||
ToplevelTypealiasesOnlyImpl(
|
ToplevelTypealiasesOnlyImpl(
|
||||||
firDiagnostic as FirPsiDiagnostic,
|
firDiagnostic as FirPsiDiagnostic,
|
||||||
|
|||||||
+5
@@ -2186,6 +2186,11 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
abstract val operator: String
|
abstract val operator: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class PropertyAsOperator : KtFirDiagnostic<PsiElement>() {
|
||||||
|
override val diagnosticClass get() = PropertyAsOperator::class
|
||||||
|
abstract val property: KtVariableSymbol
|
||||||
|
}
|
||||||
|
|
||||||
abstract class ToplevelTypealiasesOnly : KtFirDiagnostic<KtTypeAlias>() {
|
abstract class ToplevelTypealiasesOnly : KtFirDiagnostic<KtTypeAlias>() {
|
||||||
override val diagnosticClass get() = ToplevelTypealiasesOnly::class
|
override val diagnosticClass get() = ToplevelTypealiasesOnly::class
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -3522,6 +3522,14 @@ internal class AssignmentOperatorShouldReturnUnitImpl(
|
|||||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class PropertyAsOperatorImpl(
|
||||||
|
override val property: KtVariableSymbol,
|
||||||
|
firDiagnostic: FirPsiDiagnostic,
|
||||||
|
override val token: ValidityToken,
|
||||||
|
) : KtFirDiagnostic.PropertyAsOperator(), KtAbstractFirDiagnostic<PsiElement> {
|
||||||
|
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
internal class ToplevelTypealiasesOnlyImpl(
|
internal class ToplevelTypealiasesOnlyImpl(
|
||||||
firDiagnostic: FirPsiDiagnostic,
|
firDiagnostic: FirPsiDiagnostic,
|
||||||
override val token: ValidityToken,
|
override val token: ValidityToken,
|
||||||
|
|||||||
Reference in New Issue
Block a user