FIR: Make LHS of FirVariableAssignment a FirExpression

This way references can even be resolved for erroneous assignments
(e.g. function call, if expression, ... on LHS)

^KT-54648 Fixed
This commit is contained in:
Kirill Rakhman
2023-01-16 09:54:31 +01:00
committed by Space Team
parent e9b8d6db80
commit ace47c06a5
128 changed files with 950 additions and 935 deletions
@@ -31,7 +31,6 @@ fun main(args: Array<String>) {
val expressionPackage = "$basePackage.checkers.expression"
generateCheckersComponents(generationPath, expressionPackage, "FirExpressionChecker") {
alias<FirStatement>("BasicExpressionChecker")
alias<FirQualifiedAccess>("QualifiedAccessChecker")
alias<FirQualifiedAccessExpression>("QualifiedAccessExpressionChecker")
alias<FirCall>("CallChecker")
alias<FirFunctionCall>("FunctionCallChecker")
@@ -64,7 +64,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val BREAK_OR_CONTINUE_OUTSIDE_A_LOOP by error<PsiElement>()
val NOT_A_LOOP_LABEL by error<PsiElement>()
val BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY by error<KtExpressionWithLabel>()
val VARIABLE_EXPECTED by error<PsiElement>(PositioningStrategy.ASSIGNMENT_LHS)
val VARIABLE_EXPECTED by error<PsiElement>(PositioningStrategy.SELECTOR_BY_QUALIFIED)
val DELEGATION_IN_INTERFACE by error<PsiElement>()
val DELEGATION_NOT_TO_INTERFACE by error<PsiElement>()
val NESTED_CLASS_NOT_ALLOWED by error<KtNamedDeclaration>(PositioningStrategy.DECLARATION_NAME) {
@@ -91,7 +91,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val VAL_OR_VAR_ON_SECONDARY_CONSTRUCTOR_PARAMETER by error<KtParameter>(PositioningStrategy.VAL_OR_VAR_NODE) {
parameter<KtKeywordToken>("valOrVar")
}
val INVISIBLE_SETTER by error<PsiElement>(PositioningStrategy.ASSIGNMENT_LHS) {
val INVISIBLE_SETTER by error<PsiElement>(PositioningStrategy.SELECTOR_BY_QUALIFIED) {
parameter<FirPropertySymbol>("property")
parameter<Visibility>("visibility")
parameter<CallableId>("callableId")
@@ -1135,10 +1135,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val UNINITIALIZED_ENUM_COMPANION by error<KtExpression>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
parameter<FirRegularClassSymbol>("enumClass")
}
val VAL_REASSIGNMENT by error<KtExpression> {
val VAL_REASSIGNMENT by error<KtExpression>(PositioningStrategy.SELECTOR_BY_QUALIFIED) {
parameter<FirVariableSymbol<*>>("variable")
}
val VAL_REASSIGNMENT_VIA_BACKING_FIELD by deprecationError<KtExpression>(LanguageFeature.RestrictionOfValReassignmentViaBackingField) {
val VAL_REASSIGNMENT_VIA_BACKING_FIELD by deprecationError<KtExpression>(LanguageFeature.RestrictionOfValReassignmentViaBackingField, PositioningStrategy.SELECTOR_BY_QUALIFIED) {
parameter<FirBackingFieldSymbol>("property")
}
val CAPTURED_VAL_INITIALIZATION by error<KtExpression> {
@@ -1147,7 +1147,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val CAPTURED_MEMBER_VAL_INITIALIZATION by error<KtExpression> {
parameter<FirPropertySymbol>("property")
}
val SETTER_PROJECTED_OUT by error<KtBinaryExpression>(PositioningStrategy.ASSIGNMENT_LHS) {
val SETTER_PROJECTED_OUT by error<KtBinaryExpression>(PositioningStrategy.SELECTOR_BY_QUALIFIED) {
parameter<FirPropertySymbol>("property")
}
val WRONG_INVOCATION_KIND by warning<PsiElement> {
@@ -90,7 +90,6 @@ enum class PositioningStrategy(private val strategy: String? = null) {
NAME_IDENTIFIER,
QUESTION_MARK_BY_TYPE,
ANNOTATION_USE_SITE,
ASSIGNMENT_LHS,
IMPORT_LAST_NAME,
DATA_MODIFIER,
SPREAD_OPERATOR,
@@ -14,8 +14,8 @@ object JsExpressionCheckers : ExpressionCheckers() {
FirJsQualifierChecker,
)
override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker>
override val basicExpressionCheckers: Set<FirBasicExpressionChecker>
get() = setOf(
FirJsDefinedExternallyCallChecker,
FirJsDefinedExternallyCallChecker
)
}
@@ -9,17 +9,20 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.closestNonLocal
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpressionChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
import org.jetbrains.kotlin.fir.analysis.js.checkers.isNativeObject
import org.jetbrains.kotlin.fir.analysis.js.checkers.isPredefinedObject
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.expressions.calleeReference
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.name.JsStandardClassIds
object FirJsDefinedExternallyCallChecker : FirQualifiedAccessChecker() {
override fun check(expression: FirQualifiedAccess, context: CheckerContext, reporter: DiagnosticReporter) {
val symbol = expression.calleeReference.toResolvedCallableSymbol() ?: return
object FirJsDefinedExternallyCallChecker : FirBasicExpressionChecker() {
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
val symbol = expression.calleeReference?.toResolvedCallableSymbol() ?: return
if (symbol.callableId !in JsStandardClassIds.Callables.definedExternallyPropertyNames) {
return
@@ -9,7 +9,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.expression.*
import org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression.*
object JvmExpressionCheckers : ExpressionCheckers() {
override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker>
override val basicExpressionCheckers: Set<FirBasicExpressionChecker>
get() = setOf(
FirJvmProtectedInSuperClassCompanionCallChecker
)
@@ -6,20 +6,19 @@
package org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.context.findClosest
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpressionChecker
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingDeclarationSymbol
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.resolve.defaultType
@@ -29,12 +28,18 @@ import org.jetbrains.kotlin.fir.types.typeContext
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_CLASS_ID
import org.jetbrains.kotlin.types.AbstractTypeChecker
object FirJvmProtectedInSuperClassCompanionCallChecker : FirQualifiedAccessChecker() {
override fun check(expression: FirQualifiedAccess, context: CheckerContext, reporter: DiagnosticReporter) {
val dispatchReceiver = expression.dispatchReceiver
object FirJvmProtectedInSuperClassCompanionCallChecker : FirBasicExpressionChecker() {
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
val dispatchReceiver = when (expression) {
is FirQualifiedAccessExpression -> expression.dispatchReceiver
is FirVariableAssignment -> expression.dispatchReceiver
else -> null
} ?: return
if (dispatchReceiver is FirNoReceiverExpression) return
val dispatchClassSymbol = dispatchReceiver.typeRef.toRegularClassSymbol(context.session) ?: return
val resolvedSymbol = expression.calleeReference.toResolvedCallableSymbol() ?: return
val calleeReference = expression.calleeReference
val resolvedSymbol = calleeReference?.toResolvedCallableSymbol() ?: return
val visibility = if (resolvedSymbol is FirPropertySymbol) {
if (expression is FirVariableAssignment)
@@ -64,7 +69,7 @@ object FirJvmProtectedInSuperClassCompanionCallChecker : FirQualifiedAccessCheck
it.symbol == dispatchClassSymbol || it.symbol == companionContainingClassSymbol
} == null
) {
reporter.reportOn(expression.calleeReference.source, FirJvmErrors.SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC, context)
reporter.reportOn(calleeReference.source, FirJvmErrors.SUBCLASS_CANT_CALL_COMPANION_PROTECTED_NON_STATIC, context)
}
}
}
@@ -28,7 +28,7 @@ object FirJvmSuspensionPointInsideMutexLockChecker : FirFunctionCallChecker() {
if (!symbol.isSuspend) return
val closestAnonymousFunction = context.findClosest<FirAnonymousFunction>() ?: return
for (call in context.qualifiedAccessOrAnnotationCalls.asReversed()) {
for (call in context.qualifiedAccessOrAssignmentsOrAnnotationCalls.asReversed()) {
if (call is FirFunctionCall) {
val callableSymbol = call.calleeReference.toResolvedCallableSymbol() ?: continue
if (callableSymbol.callableId == synchronizedCallableId) {
@@ -15,8 +15,6 @@ import org.jetbrains.kotlin.fir.analysis.CheckersComponentInternal
class ComposedExpressionCheckers : ExpressionCheckers() {
override val basicExpressionCheckers: Set<FirBasicExpressionChecker>
get() = _basicExpressionCheckers
override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker>
get() = _qualifiedAccessCheckers
override val qualifiedAccessExpressionCheckers: Set<FirQualifiedAccessExpressionChecker>
get() = _qualifiedAccessExpressionCheckers
override val callCheckers: Set<FirCallChecker>
@@ -81,7 +79,6 @@ class ComposedExpressionCheckers : ExpressionCheckers() {
get() = _classReferenceExpressionCheckers
private val _basicExpressionCheckers: MutableSet<FirBasicExpressionChecker> = mutableSetOf()
private val _qualifiedAccessCheckers: MutableSet<FirQualifiedAccessChecker> = mutableSetOf()
private val _qualifiedAccessExpressionCheckers: MutableSet<FirQualifiedAccessExpressionChecker> = mutableSetOf()
private val _callCheckers: MutableSet<FirCallChecker> = mutableSetOf()
private val _functionCallCheckers: MutableSet<FirFunctionCallChecker> = mutableSetOf()
@@ -117,7 +114,6 @@ class ComposedExpressionCheckers : ExpressionCheckers() {
@CheckersComponentInternal
fun register(checkers: ExpressionCheckers) {
_basicExpressionCheckers += checkers.basicExpressionCheckers
_qualifiedAccessCheckers += checkers.qualifiedAccessCheckers
_qualifiedAccessExpressionCheckers += checkers.qualifiedAccessExpressionCheckers
_callCheckers += checkers.callCheckers
_functionCallCheckers += checkers.functionCallCheckers
@@ -18,7 +18,6 @@ abstract class ExpressionCheckers {
}
open val basicExpressionCheckers: Set<FirBasicExpressionChecker> = emptySet()
open val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker> = emptySet()
open val qualifiedAccessExpressionCheckers: Set<FirQualifiedAccessExpressionChecker> = emptySet()
open val callCheckers: Set<FirCallChecker> = emptySet()
open val functionCallCheckers: Set<FirFunctionCallChecker> = emptySet()
@@ -52,13 +51,12 @@ abstract class ExpressionCheckers {
open val classReferenceExpressionCheckers: Set<FirClassReferenceExpressionChecker> = emptySet()
@CheckersComponentInternal internal val allBasicExpressionCheckers: Set<FirBasicExpressionChecker> by lazy { basicExpressionCheckers }
@CheckersComponentInternal internal val allQualifiedAccessCheckers: Set<FirQualifiedAccessChecker> by lazy { qualifiedAccessCheckers + basicExpressionCheckers }
@CheckersComponentInternal internal val allQualifiedAccessExpressionCheckers: Set<FirQualifiedAccessExpressionChecker> by lazy { qualifiedAccessExpressionCheckers + basicExpressionCheckers + qualifiedAccessCheckers }
@CheckersComponentInternal internal val allQualifiedAccessExpressionCheckers: Set<FirQualifiedAccessExpressionChecker> by lazy { qualifiedAccessExpressionCheckers + basicExpressionCheckers }
@CheckersComponentInternal internal val allCallCheckers: Set<FirCallChecker> by lazy { callCheckers + basicExpressionCheckers }
@CheckersComponentInternal internal val allFunctionCallCheckers: Set<FirFunctionCallChecker> by lazy { functionCallCheckers + qualifiedAccessExpressionCheckers + basicExpressionCheckers + qualifiedAccessCheckers + callCheckers }
@CheckersComponentInternal internal val allPropertyAccessExpressionCheckers: Set<FirPropertyAccessExpressionChecker> by lazy { propertyAccessExpressionCheckers + qualifiedAccessExpressionCheckers + basicExpressionCheckers + qualifiedAccessCheckers }
@CheckersComponentInternal internal val allIntegerLiteralOperatorCallCheckers: Set<FirIntegerLiteralOperatorCallChecker> by lazy { integerLiteralOperatorCallCheckers + functionCallCheckers + qualifiedAccessExpressionCheckers + basicExpressionCheckers + qualifiedAccessCheckers + callCheckers }
@CheckersComponentInternal internal val allVariableAssignmentCheckers: Set<FirVariableAssignmentChecker> by lazy { variableAssignmentCheckers + qualifiedAccessCheckers + basicExpressionCheckers }
@CheckersComponentInternal internal val allFunctionCallCheckers: Set<FirFunctionCallChecker> by lazy { functionCallCheckers + qualifiedAccessExpressionCheckers + basicExpressionCheckers + callCheckers }
@CheckersComponentInternal internal val allPropertyAccessExpressionCheckers: Set<FirPropertyAccessExpressionChecker> by lazy { propertyAccessExpressionCheckers + qualifiedAccessExpressionCheckers + basicExpressionCheckers }
@CheckersComponentInternal internal val allIntegerLiteralOperatorCallCheckers: Set<FirIntegerLiteralOperatorCallChecker> by lazy { integerLiteralOperatorCallCheckers + functionCallCheckers + qualifiedAccessExpressionCheckers + basicExpressionCheckers + callCheckers }
@CheckersComponentInternal internal val allVariableAssignmentCheckers: Set<FirVariableAssignmentChecker> by lazy { variableAssignmentCheckers + basicExpressionCheckers }
@CheckersComponentInternal internal val allTryExpressionCheckers: Set<FirTryExpressionChecker> by lazy { tryExpressionCheckers + basicExpressionCheckers }
@CheckersComponentInternal internal val allWhenExpressionCheckers: Set<FirWhenExpressionChecker> by lazy { whenExpressionCheckers + basicExpressionCheckers }
@CheckersComponentInternal internal val allLoopExpressionCheckers: Set<FirLoopExpressionChecker> by lazy { loopExpressionCheckers + basicExpressionCheckers }
@@ -77,8 +75,8 @@ abstract class ExpressionCheckers {
@CheckersComponentInternal internal val allTypeOperatorCallCheckers: Set<FirTypeOperatorCallChecker> by lazy { typeOperatorCallCheckers + basicExpressionCheckers + callCheckers }
@CheckersComponentInternal internal val allResolvedQualifierCheckers: Set<FirResolvedQualifierChecker> by lazy { resolvedQualifierCheckers + basicExpressionCheckers }
@CheckersComponentInternal internal val allConstExpressionCheckers: Set<FirConstExpressionChecker> by lazy { constExpressionCheckers + basicExpressionCheckers }
@CheckersComponentInternal internal val allCallableReferenceAccessCheckers: Set<FirCallableReferenceAccessChecker> by lazy { callableReferenceAccessCheckers + qualifiedAccessExpressionCheckers + basicExpressionCheckers + qualifiedAccessCheckers }
@CheckersComponentInternal internal val allThisReceiverExpressionCheckers: Set<FirThisReceiverExpressionChecker> by lazy { thisReceiverExpressionCheckers + qualifiedAccessExpressionCheckers + basicExpressionCheckers + qualifiedAccessCheckers }
@CheckersComponentInternal internal val allCallableReferenceAccessCheckers: Set<FirCallableReferenceAccessChecker> by lazy { callableReferenceAccessCheckers + qualifiedAccessExpressionCheckers + basicExpressionCheckers }
@CheckersComponentInternal internal val allThisReceiverExpressionCheckers: Set<FirThisReceiverExpressionChecker> by lazy { thisReceiverExpressionCheckers + qualifiedAccessExpressionCheckers + basicExpressionCheckers }
@CheckersComponentInternal internal val allWhileLoopCheckers: Set<FirWhileLoopChecker> by lazy { whileLoopCheckers + loopExpressionCheckers + basicExpressionCheckers }
@CheckersComponentInternal internal val allThrowExpressionCheckers: Set<FirThrowExpressionChecker> by lazy { throwExpressionCheckers + basicExpressionCheckers }
@CheckersComponentInternal internal val allDoWhileLoopCheckers: Set<FirDoWhileLoopChecker> by lazy { doWhileLoopCheckers + loopExpressionCheckers + basicExpressionCheckers }
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.fir.expressions.FirIntegerLiteralOperatorCall
import org.jetbrains.kotlin.fir.expressions.FirLoop
import org.jetbrains.kotlin.fir.expressions.FirLoopJump
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
@@ -45,7 +44,6 @@ import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
import org.jetbrains.kotlin.fir.expressions.FirWhileLoop
typealias FirBasicExpressionChecker = FirExpressionChecker<FirStatement>
typealias FirQualifiedAccessChecker = FirExpressionChecker<FirQualifiedAccess>
typealias FirQualifiedAccessExpressionChecker = FirExpressionChecker<FirQualifiedAccessExpression>
typealias FirCallChecker = FirExpressionChecker<FirCall>
typealias FirFunctionCallChecker = FirExpressionChecker<FirFunctionCall>
@@ -123,7 +123,7 @@ object FirErrors {
val BREAK_OR_CONTINUE_OUTSIDE_A_LOOP by error0<PsiElement>()
val NOT_A_LOOP_LABEL by error0<PsiElement>()
val BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY by error0<KtExpressionWithLabel>()
val VARIABLE_EXPECTED by error0<PsiElement>(SourceElementPositioningStrategies.ASSIGNMENT_LHS)
val VARIABLE_EXPECTED by error0<PsiElement>(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED)
val DELEGATION_IN_INTERFACE by error0<PsiElement>()
val DELEGATION_NOT_TO_INTERFACE by error0<PsiElement>()
val NESTED_CLASS_NOT_ALLOWED by error1<KtNamedDeclaration, String>(SourceElementPositioningStrategies.DECLARATION_NAME)
@@ -140,7 +140,7 @@ object FirErrors {
val VAL_OR_VAR_ON_FUN_PARAMETER by error1<KtParameter, KtKeywordToken>(SourceElementPositioningStrategies.VAL_OR_VAR_NODE)
val VAL_OR_VAR_ON_CATCH_PARAMETER by error1<KtParameter, KtKeywordToken>(SourceElementPositioningStrategies.VAL_OR_VAR_NODE)
val VAL_OR_VAR_ON_SECONDARY_CONSTRUCTOR_PARAMETER by error1<KtParameter, KtKeywordToken>(SourceElementPositioningStrategies.VAL_OR_VAR_NODE)
val INVISIBLE_SETTER by error3<PsiElement, FirPropertySymbol, Visibility, CallableId>(SourceElementPositioningStrategies.ASSIGNMENT_LHS)
val INVISIBLE_SETTER by error3<PsiElement, FirPropertySymbol, Visibility, CallableId>(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED)
// Unresolved
val INVISIBLE_REFERENCE by error1<PsiElement, FirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -607,11 +607,11 @@ object FirErrors {
val UNINITIALIZED_PARAMETER by error1<KtSimpleNameExpression, FirValueParameterSymbol>()
val UNINITIALIZED_ENUM_ENTRY by error1<KtSimpleNameExpression, FirEnumEntrySymbol>()
val UNINITIALIZED_ENUM_COMPANION by error1<KtExpression, FirRegularClassSymbol>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val VAL_REASSIGNMENT by error1<KtExpression, FirVariableSymbol<*>>()
val VAL_REASSIGNMENT_VIA_BACKING_FIELD by deprecationError1<KtExpression, FirBackingFieldSymbol>(RestrictionOfValReassignmentViaBackingField)
val VAL_REASSIGNMENT by error1<KtExpression, FirVariableSymbol<*>>(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED)
val VAL_REASSIGNMENT_VIA_BACKING_FIELD by deprecationError1<KtExpression, FirBackingFieldSymbol>(RestrictionOfValReassignmentViaBackingField, SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED)
val CAPTURED_VAL_INITIALIZATION by error1<KtExpression, FirPropertySymbol>()
val CAPTURED_MEMBER_VAL_INITIALIZATION by error1<KtExpression, FirPropertySymbol>()
val SETTER_PROJECTED_OUT by error1<KtBinaryExpression, FirPropertySymbol>(SourceElementPositioningStrategies.ASSIGNMENT_LHS)
val SETTER_PROJECTED_OUT by error1<KtBinaryExpression, FirPropertySymbol>(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED)
val WRONG_INVOCATION_KIND by warning3<PsiElement, FirBasedSymbol<*>, EventOccurrencesRange, EventOccurrencesRange>()
val LEAKED_IN_PLACE_LAMBDA by warning1<PsiElement, FirBasedSymbol<*>>()
val WRONG_IMPLIES_CONDITION by warning0<PsiElement>()
@@ -22,11 +22,8 @@ import org.jetbrains.kotlin.fir.contracts.effects
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirContractDescriptionOwner
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.FirThisReference
@@ -309,7 +306,7 @@ object FirCallsEffectAnalyzer : FirControlFlowChecker() {
return this is FirAnonymousFunction && this.isLambda && this.invocationKind != null
}
private fun FirExpression?.toQualifiedReference(): FirReference? = (this as? FirQualifiedAccess)?.calleeReference
private fun FirExpression?.toQualifiedReference(): FirReference? = (this as? FirQualifiedAccessExpression)?.calleeReference
private fun referenceToSymbol(reference: FirReference?): FirBasedSymbol<*>? = when (reference) {
is FirResolvedNamedReference -> reference.resolvedSymbol
@@ -18,8 +18,10 @@ import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField
import org.jetbrains.kotlin.fir.declarations.utils.hasExplicitBackingField
import org.jetbrains.kotlin.fir.declarations.utils.isLateInit
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
import org.jetbrains.kotlin.fir.expressions.calleeReference
import org.jetbrains.kotlin.fir.expressions.unwrapLValue
import org.jetbrains.kotlin.fir.isCatchParameter
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
@@ -65,7 +67,7 @@ private fun PropertyInitializationInfoData.checkPropertyAccesses(
scope: FirDeclaration?,
scopes: MutableMap<FirPropertySymbol, FirDeclaration?>,
) {
fun FirQualifiedAccess.hasCorrectReceiver() =
fun FirQualifiedAccessExpression.hasCorrectReceiver() =
(dispatchReceiver as? FirThisReceiverExpression)?.calleeReference?.boundSymbol == receiver
for (node in graph.nodes) {
@@ -83,17 +85,17 @@ private fun PropertyInitializationInfoData.checkPropertyAccesses(
}
node is VariableAssignmentNode -> {
val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: continue
if (!symbol.fir.isVal || !node.fir.hasCorrectReceiver() || symbol !in properties) continue
val symbol = node.fir.calleeReference?.toResolvedPropertySymbol() ?: continue
if (!symbol.fir.isVal || node.fir.unwrapLValue()?.hasCorrectReceiver() != true || symbol !in properties) continue
if (scope != scopes[symbol]) {
if (getValue(node).values.any { it[symbol]?.canBeRevisited() == true }) {
reporter.reportOn(node.fir.lValue.source, FirErrors.VAL_REASSIGNMENT, symbol, context)
} else if (scope != scopes[symbol]) {
val error = if (receiver != null)
FirErrors.CAPTURED_MEMBER_VAL_INITIALIZATION
else
FirErrors.CAPTURED_VAL_INITIALIZATION
reporter.reportOn(node.fir.lValue.source, error, symbol, context)
} else if (getValue(node).values.any { it[symbol]?.canBeRevisited() == true }) {
reporter.reportOn(node.fir.lValue.source, FirErrors.VAL_REASSIGNMENT, symbol, context)
}
}
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.fir.analysis.cfa.util
import kotlinx.collections.immutable.persistentMapOf
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
import org.jetbrains.kotlin.fir.expressions.calleeReference
import org.jetbrains.kotlin.fir.expressions.dispatchReceiver
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
@@ -51,7 +53,7 @@ class PropertyInitializationInfoCollector(
val dataForNode = visitNode(node, data)
val receiver = (node.fir.dispatchReceiver as? FirThisReceiverExpression)?.calleeReference?.boundSymbol
if (receiver != expectedReceiver) return dataForNode
val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: return dataForNode
val symbol = node.fir.calleeReference?.toResolvedPropertySymbol() ?: return dataForNode
if (symbol !in localProperties) return dataForNode
return addRange(dataForNode, symbol, EventOccurrencesRange.EXACTLY_ONCE)
}
@@ -22,7 +22,8 @@ object CommonExpressionCheckers : ExpressionCheckers() {
FirUnderscoreChecker,
FirExpressionAnnotationChecker,
FirDeprecationChecker,
FirRecursiveProblemChecker
FirRecursiveProblemChecker,
FirOptInUsageAccessChecker,
)
override val throwExpressionCheckers: Set<FirThrowExpressionChecker>
@@ -30,11 +31,6 @@ object CommonExpressionCheckers : ExpressionCheckers() {
FirThrowExpressionTypeChecker,
)
override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker>
get() = setOf(
FirOptInUsageAccessChecker,
)
override val qualifiedAccessExpressionCheckers: Set<FirQualifiedAccessExpressionChecker>
get() = setOf(
FirCallableReferenceChecker,
@@ -83,7 +79,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
override val variableAssignmentCheckers: Set<FirVariableAssignmentChecker>
get() = setOf(
FirReassignmentAndInvisibleSetterChecker,
FirAssignmentTypeMismatchChecker
FirAssignmentTypeMismatchChecker,
)
override val whenExpressionCheckers: Set<FirWhenExpressionChecker>
@@ -655,7 +655,7 @@ fun getActualTargetList(annotated: FirDeclaration): AnnotationTargetList {
private typealias TargetLists = AnnotationTargetLists
fun FirQualifiedAccess.explicitReceiverIsNotSuperReference(): Boolean {
fun FirQualifiedAccessExpression.explicitReceiverIsNotSuperReference(): Boolean {
return (this.explicitReceiver as? FirQualifiedAccessExpression)?.calleeReference !is FirSuperReference
}
@@ -728,3 +728,9 @@ fun FirBasedSymbol<*>.getAnnotationStringParameter(classId: ClassId, session: Fi
val expression = annotation?.argumentMapping?.mapping?.values?.firstOrNull() as? FirConstExpression<*>
return expression?.value as? String
}
fun FirElement.isLhsOfAssignment(context: CheckerContext): Boolean {
if (this !is FirQualifiedAccessExpression) return false
val lastQualified = context.qualifiedAccessOrAssignmentsOrAnnotationCalls.lastOrNull { it != this } ?: return false
return lastQualified is FirVariableAssignment && lastQualified.lValue == this
}
@@ -31,7 +31,7 @@ abstract class CheckerContext : MutableDiagnosticContext() {
// Context
abstract val implicitReceiverStack: ImplicitReceiverStack
abstract val containingDeclarations: List<FirDeclaration>
abstract val qualifiedAccessOrAnnotationCalls: List<FirStatement>
abstract val qualifiedAccessOrAssignmentsOrAnnotationCalls: List<FirStatement>
abstract val getClassCalls: List<FirGetClassCall>
abstract val annotationContainers: List<FirAnnotationContainer>
abstract val isContractBody: Boolean
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.name.Name
class MutableCheckerContext private constructor(
override val implicitReceiverStack: PersistentImplicitReceiverStack,
override val containingDeclarations: MutableList<FirDeclaration>,
override val qualifiedAccessOrAnnotationCalls: MutableList<FirStatement>,
override val qualifiedAccessOrAssignmentsOrAnnotationCalls: MutableList<FirStatement>,
override val getClassCalls: MutableList<FirGetClassCall>,
override val annotationContainers: MutableList<FirAnnotationContainer>,
override var isContractBody: Boolean,
@@ -50,7 +50,7 @@ class MutableCheckerContext private constructor(
return MutableCheckerContext(
implicitReceiverStack.add(name, value),
containingDeclarations,
qualifiedAccessOrAnnotationCalls,
qualifiedAccessOrAssignmentsOrAnnotationCalls,
getClassCalls,
annotationContainers,
isContractBody,
@@ -73,12 +73,12 @@ class MutableCheckerContext private constructor(
}
override fun addQualifiedAccessOrAnnotationCall(qualifiedAccessOrAnnotationCall: FirStatement): MutableCheckerContext {
qualifiedAccessOrAnnotationCalls.add(qualifiedAccessOrAnnotationCall)
qualifiedAccessOrAssignmentsOrAnnotationCalls.add(qualifiedAccessOrAnnotationCall)
return this
}
override fun dropQualifiedAccessOrAnnotationCall() {
qualifiedAccessOrAnnotationCalls.removeAt(qualifiedAccessOrAnnotationCalls.size - 1)
qualifiedAccessOrAssignmentsOrAnnotationCalls.removeAt(qualifiedAccessOrAssignmentsOrAnnotationCalls.size - 1)
}
override fun addGetClassCall(getClassCall: FirGetClassCall): MutableCheckerContext {
@@ -109,7 +109,7 @@ class MutableCheckerContext private constructor(
return MutableCheckerContext(
implicitReceiverStack,
containingDeclarations,
qualifiedAccessOrAnnotationCalls,
qualifiedAccessOrAssignmentsOrAnnotationCalls,
getClassCalls,
annotationContainers,
isContractBody,
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.name.Name
class PersistentCheckerContext private constructor(
override val implicitReceiverStack: PersistentImplicitReceiverStack,
override val containingDeclarations: PersistentList<FirDeclaration>,
override val qualifiedAccessOrAnnotationCalls: PersistentList<FirStatement>,
override val qualifiedAccessOrAssignmentsOrAnnotationCalls: PersistentList<FirStatement>,
override val getClassCalls: PersistentList<FirGetClassCall>,
override val annotationContainers: PersistentList<FirAnnotationContainer>,
override val isContractBody: Boolean,
@@ -52,7 +52,7 @@ class PersistentCheckerContext private constructor(
return PersistentCheckerContext(
implicitReceiverStack.add(name, value),
containingDeclarations,
qualifiedAccessOrAnnotationCalls,
qualifiedAccessOrAssignmentsOrAnnotationCalls,
getClassCalls,
annotationContainers,
isContractBody,
@@ -69,7 +69,7 @@ class PersistentCheckerContext private constructor(
return PersistentCheckerContext(
implicitReceiverStack,
containingDeclarations.add(declaration),
qualifiedAccessOrAnnotationCalls,
qualifiedAccessOrAssignmentsOrAnnotationCalls,
getClassCalls,
annotationContainers,
isContractBody,
@@ -89,7 +89,7 @@ class PersistentCheckerContext private constructor(
return PersistentCheckerContext(
implicitReceiverStack,
containingDeclarations,
this.qualifiedAccessOrAnnotationCalls.add(qualifiedAccessOrAnnotationCall),
this.qualifiedAccessOrAssignmentsOrAnnotationCalls.add(qualifiedAccessOrAnnotationCall),
getClassCalls,
annotationContainers,
isContractBody,
@@ -109,7 +109,7 @@ class PersistentCheckerContext private constructor(
return PersistentCheckerContext(
implicitReceiverStack,
containingDeclarations,
qualifiedAccessOrAnnotationCalls,
qualifiedAccessOrAssignmentsOrAnnotationCalls,
getClassCalls.add(getClassCall),
annotationContainers,
isContractBody,
@@ -129,7 +129,7 @@ class PersistentCheckerContext private constructor(
return PersistentCheckerContext(
implicitReceiverStack,
containingDeclarations,
qualifiedAccessOrAnnotationCalls,
qualifiedAccessOrAssignmentsOrAnnotationCalls,
getClassCalls,
annotationContainers.add(annotationContainer),
isContractBody,
@@ -155,7 +155,7 @@ class PersistentCheckerContext private constructor(
return PersistentCheckerContext(
implicitReceiverStack,
containingDeclarations,
qualifiedAccessOrAnnotationCalls,
qualifiedAccessOrAssignmentsOrAnnotationCalls,
getClassCalls,
annotationContainers,
isContractBody,
@@ -174,7 +174,7 @@ class PersistentCheckerContext private constructor(
return PersistentCheckerContext(
implicitReceiverStack,
containingDeclarations,
qualifiedAccessOrAnnotationCalls,
qualifiedAccessOrAssignmentsOrAnnotationCalls,
getClassCalls,
annotationContainers,
isContractBody = newValue,
@@ -182,7 +182,7 @@ object FirDestructuringDeclarationChecker : FirPropertyChecker() {
}
private val FirExpression.explicitReceiverOfQualifiedAccess: FirQualifiedAccessExpression?
get() = (this as? FirQualifiedAccess)?.explicitReceiver?.unwrapped as? FirQualifiedAccessExpression
get() = (this as? FirQualifiedAccessExpression)?.explicitReceiver?.unwrapped as? FirQualifiedAccessExpression
private val FirExpression.unwrapped: FirExpression
get() =
@@ -111,7 +111,7 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
override fun visitSmartCastExpression(smartCastExpression: FirSmartCastExpression, data: CheckerContext) {}
override fun visitVariableAssignment(variableAssignment: FirVariableAssignment, data: CheckerContext) {
val propertySymbol = variableAssignment.calleeReference.toResolvedCallableSymbol() as? FirPropertySymbol ?: return
val propertySymbol = variableAssignment.calleeReference?.toResolvedCallableSymbol() as? FirPropertySymbol ?: return
val setterSymbol = propertySymbol.setterSymbol ?: return
checkQualifiedAccess(variableAssignment, setterSymbol, data)
}
@@ -237,7 +237,7 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
}
private fun checkQualifiedAccess(
qualifiedAccess: FirQualifiedAccess,
qualifiedAccess: FirStatement,
targetSymbol: FirBasedSymbol<*>?,
context: CheckerContext
) {
@@ -253,18 +253,18 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
checkRecursion(targetSymbol, source, context)
}
private fun FirQualifiedAccess.partOfCall(context: CheckerContext): Boolean {
private fun FirStatement.partOfCall(context: CheckerContext): Boolean {
if (this !is FirExpression) return false
val containingQualifiedAccess = context.qualifiedAccessOrAnnotationCalls.getOrNull(
context.qualifiedAccessOrAnnotationCalls.size - 2
val containingQualifiedAccess = context.qualifiedAccessOrAssignmentsOrAnnotationCalls.getOrNull(
context.qualifiedAccessOrAssignmentsOrAnnotationCalls.size - 2
) ?: return false
if (this == (containingQualifiedAccess as? FirQualifiedAccess)?.explicitReceiver) return true
if (this == (containingQualifiedAccess as? FirQualifiedAccessExpression)?.explicitReceiver) return true
val call = containingQualifiedAccess as? FirCall ?: return false
return call.arguments.any { it.unwrapArgument() == this }
}
private fun checkVisibilityAndAccess(
accessExpression: FirQualifiedAccess,
accessExpression: FirStatement,
calledDeclaration: FirCallableSymbol<*>?,
source: KtSourceElement,
context: CheckerContext
@@ -321,16 +321,21 @@ abstract class FirInlineDeclarationChecker : FirFunctionChecker() {
private fun checkSuperCalls(
calledDeclaration: FirCallableSymbol<*>,
callExpression: FirQualifiedAccess,
callExpression: FirStatement,
context: CheckerContext
) {
val receiver = callExpression.dispatchReceiver as? FirQualifiedAccessExpression ?: return
val receiver = when (callExpression) {
is FirQualifiedAccessExpression -> callExpression.dispatchReceiver
is FirVariableAssignment -> callExpression.dispatchReceiver
else -> null
} as? FirQualifiedAccessExpression ?: return
if (receiver.calleeReference is FirSuperReference) {
val dispatchReceiverType = receiver.dispatchReceiver.typeRef.coneType
val classSymbol = dispatchReceiverType.toSymbol(session) ?: return
if (!classSymbol.isDefinedInInlineFunction()) {
reporter.reportOn(
callExpression.dispatchReceiver.source,
receiver.source,
FirErrors.SUPER_CALL_FROM_PUBLIC_INLINE,
calledDeclaration,
context
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.expressions.calleeReference
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
@@ -38,7 +39,7 @@ object FirPropertyInitializationChecker : FirRegularClassChecker() {
override fun visitVariableAssignment(variableAssignment: FirVariableAssignment) {
variableAssignment.acceptChildren(this)
val propertySymbol = variableAssignment.lValue.toResolvedCallableSymbol() as? FirPropertySymbol ?: return
val propertySymbol = variableAssignment.calleeReference?.toResolvedCallableSymbol() as? FirPropertySymbol ?: return
if (propertySymbol !in declaredLater) return
reporter.reportOn(variableAssignment.lValue.source, FirErrors.INITIALIZATION_BEFORE_DECLARATION, propertySymbol, context)
}
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.fir.types.coneType
object FirAssignmentTypeMismatchChecker : FirVariableAssignmentChecker() {
override fun check(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) {
val source = expression.rValue.source ?: return
val coneType = expression.lValueTypeRef.coneType
val coneType = expression.lValue.typeRef.coneType
checkTypeMismatch(
coneType,
expression,
@@ -29,7 +29,7 @@ object FirConstructorCallChecker : FirFunctionCallChecker() {
reporter.reportOn(expression.source, FirErrors.CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS, context)
}
if (declarationClass.classKind == ClassKind.ANNOTATION_CLASS &&
context.qualifiedAccessOrAnnotationCalls.all { call ->
context.qualifiedAccessOrAssignmentsOrAnnotationCalls.all { call ->
call !is FirAnnotation
} &&
context.containingDeclarations.all { klass ->
@@ -15,11 +15,9 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.FutureApiDeprecationInfo
import org.jetbrains.kotlin.fir.analysis.checkers.isLhsOfAssignment
import org.jetbrains.kotlin.fir.declarations.getDeprecation
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirDelegatedConstructorCall
import org.jetbrains.kotlin.fir.expressions.FirResolvable
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.resolved
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
@@ -36,11 +34,13 @@ object FirDeprecationChecker : FirBasicExpressionChecker() {
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
if (!allowedSourceKinds.contains(expression.source?.kind)) return
if (expression is FirAnnotation || expression is FirDelegatedConstructorCall) return //checked by FirDeprecatedTypeChecker
val resolvable = expression as? FirResolvable ?: return
val reference = resolvable.calleeReference.resolved ?: return
val referencedSymbol = reference.resolvedSymbol
if (expression.isLhsOfAssignment(context)) return
reportApiStatusIfNeeded(reference.source, referencedSymbol, expression, context, reporter)
val calleeReference = expression.calleeReference ?: return
val resolvedReference = calleeReference.resolved ?: return
val referencedSymbol = resolvedReference.resolvedSymbol
reportApiStatusIfNeeded(resolvedReference.source, referencedSymbol, expression, context, reporter)
}
internal fun reportApiStatusIfNeeded(
@@ -8,33 +8,37 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.analysis.checkers.isLhsOfAssignment
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.references.toResolvedBaseSymbol
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.coneType
object FirOptInUsageAccessChecker : FirQualifiedAccessChecker() {
override fun check(expression: FirQualifiedAccess, context: CheckerContext, reporter: DiagnosticReporter) {
object FirOptInUsageAccessChecker : FirBasicExpressionChecker() {
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
val sourceKind = expression.source?.kind
if (sourceKind is KtFakeSourceElementKind.DataClassGeneratedMembers ||
sourceKind is KtFakeSourceElementKind.PropertyFromParameter
) return
val resolvedSymbol = expression.calleeReference.toResolvedBaseSymbol() ?: return
val dispatchReceiverType =
expression.dispatchReceiver.takeIf { it !is FirNoReceiverExpression }?.typeRef?.coneType?.fullyExpandedType(context.session)
if (expression.isLhsOfAssignment(context)) return
val resolvedSymbol = expression.calleeReference?.toResolvedBaseSymbol() ?: return
with(FirOptInUsageBaseChecker) {
if (expression is FirVariableAssignment && resolvedSymbol is FirPropertySymbol) {
val experimentalities = resolvedSymbol.loadExperimentalities(context, fromSetter = true, dispatchReceiverType) +
loadExperimentalitiesFromTypeArguments(context, expression.typeArguments)
if (expression is FirVariableAssignment) {
val experimentalities = resolvedSymbol.loadExperimentalities(context, fromSetter = true, null) +
loadExperimentalitiesFromTypeArguments(context, emptyList())
reportNotAcceptedExperimentalities(experimentalities, expression.lValue, context, reporter)
return
} else if (expression is FirQualifiedAccessExpression) {
val dispatchReceiverType =
expression.dispatchReceiver.takeIf { it !is FirNoReceiverExpression }?.typeRef?.coneType?.fullyExpandedType(context.session)
val experimentalities = resolvedSymbol.loadExperimentalities(context, fromSetter = false, dispatchReceiverType) +
loadExperimentalitiesFromTypeArguments(context, expression.typeArguments)
reportNotAcceptedExperimentalities(experimentalities, expression, context, reporter)
}
val experimentalities = resolvedSymbol.loadExperimentalities(context, fromSetter = false, dispatchReceiverType) +
loadExperimentalitiesFromTypeArguments(context, expression.typeArguments)
reportNotAcceptedExperimentalities(experimentalities, expression, context, reporter)
}
}
}
@@ -16,17 +16,14 @@ import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirSmartCastExpression
import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.originalForSubstitutionOverride
import org.jetbrains.kotlin.fir.references.FirBackingFieldReference
import org.jetbrains.kotlin.fir.references.FirThisReference
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
import org.jetbrains.kotlin.fir.references.toResolvedValueParameterSymbol
import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.resolve.calls.ExpressionReceiverValue
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeDiagnosticWithCandidates
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.visibilityChecker
@@ -36,7 +33,7 @@ object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker()
checkInvisibleSetter(expression, context, reporter)
checkValReassignmentViaBackingField(expression, context, reporter)
checkValReassignmentOnValueParameter(expression, context, reporter)
checkAssignmentToThis(expression, context, reporter)
checkVariableExpected(expression, context, reporter)
checkValReassignment(expression, context, reporter)
}
@@ -61,7 +58,7 @@ object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker()
return false
}
val callableSymbol = expression.calleeReference.toResolvedCallableSymbol()
val callableSymbol = expression.calleeReference?.toResolvedCallableSymbol()
if (callableSymbol is FirPropertySymbol && shouldInvisibleSetterBeReported(callableSymbol)) {
val explicitReceiver = expression.explicitReceiver
// Try to get type from smartcast
@@ -78,7 +75,7 @@ object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker()
}
}
reporter.reportOn(
expression.source,
expression.lValue.source,
FirErrors.INVISIBLE_SETTER,
callableSymbol,
callableSymbol.setterSymbol?.visibility ?: Visibilities.Private,
@@ -93,7 +90,7 @@ object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker()
context: CheckerContext,
reporter: DiagnosticReporter
) {
val backingFieldReference = expression.lValue as? FirBackingFieldReference ?: return
val backingFieldReference = expression.calleeReference as? FirBackingFieldReference ?: return
val propertySymbol = backingFieldReference.resolvedSymbol
if (propertySymbol.isVar) return
val closestGetter = context.findClosest<FirPropertyAccessor> { it.isGetter }?.symbol ?: return
@@ -107,22 +104,37 @@ object FirReassignmentAndInvisibleSetterChecker : FirVariableAssignmentChecker()
context: CheckerContext,
reporter: DiagnosticReporter
) {
val valueParameter = expression.lValue.toResolvedValueParameterSymbol() ?: return
val valueParameter = expression.calleeReference?.toResolvedValueParameterSymbol() ?: return
reporter.reportOn(expression.lValue.source, FirErrors.VAL_REASSIGNMENT, valueParameter, context)
}
private fun checkAssignmentToThis(
private fun checkVariableExpected(
expression: FirVariableAssignment,
context: CheckerContext,
reporter: DiagnosticReporter
) {
if (expression.lValue is FirThisReference) {
val calleeReference = expression.calleeReference
if (expression.unwrapLValue() !is FirPropertyAccessExpression ||
(calleeReference?.isConflictingError() != true && calleeReference?.toResolvedVariableSymbol() == null)
) {
reporter.reportOn(expression.lValue.source, FirErrors.VARIABLE_EXPECTED, context)
}
}
private fun FirReference.isConflictingError(): Boolean {
if (!isError()) return false
return when (val it = diagnostic) {
is ConeSimpleDiagnostic -> it.kind == DiagnosticKind.VariableExpected
is ConeUnresolvedNameError -> true
is ConeDiagnosticWithCandidates -> it.candidates.any { it.symbol is FirPropertySymbol }
else -> false
}
}
private fun checkValReassignment(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) {
val property = expression.lValue.toResolvedPropertySymbol() ?: return
val property = expression.calleeReference?.toResolvedPropertySymbol() ?: return
if (property.isVar) return
// Assignments of uninitialized `val`s must be checked via CFG, since the first one is OK.
// See `FirPropertyInitializationAnalyzer` for locals and `FirMemberPropertiesChecker` for backing fields in initializers.
@@ -36,7 +36,7 @@ object FirReturnSyntaxAndLabelChecker : FirReturnExpressionChecker() {
if (targetSymbol is FirAnonymousFunctionSymbol) {
val label = targetSymbol.label
if (label?.source?.kind !is KtRealSourceElementKind) {
val functionCall = context.qualifiedAccessOrAnnotationCalls.asReversed().find {
val functionCall = context.qualifiedAccessOrAssignmentsOrAnnotationCalls.asReversed().find {
it is FirFunctionCall &&
(it.calleeReference.toResolvedFunctionSymbol())?.callableId ==
FirSuspendCallChecker.KOTLIN_SUSPEND_BUILT_IN_FUNCTION_CALLABLE_ID
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.fullyExpandedClass
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.fir.types.isUnit
object FirStandaloneQualifierChecker : FirResolvedQualifierChecker() {
override fun check(expression: FirResolvedQualifier, context: CheckerContext, reporter: DiagnosticReporter) {
val lastQualifiedAccess = context.qualifiedAccessOrAnnotationCalls.lastOrNull() as? FirQualifiedAccess
val lastQualifiedAccess = context.qualifiedAccessOrAssignmentsOrAnnotationCalls.lastOrNull() as? FirQualifiedAccessExpression
// Note: qualifier isn't standalone when it's in receiver (SomeClass.foo) or getClass (SomeClass::class) position
if (lastQualifiedAccess?.explicitReceiver === expression || lastQualifiedAccess?.dispatchReceiver === expression) return
val lastGetClass = context.getClassCalls.lastOrNull()
@@ -33,7 +33,7 @@ object FirTypeParameterInQualifiedAccessChecker : FirQualifiedAccessExpressionCh
// Make sure the current expression is not the receiver of a qualified access expression.
// E.g., for `T::toString`, which is a callable reference (a subtype of qualified access), type parameter T is checked once as an
// explicit receiver. When we visit `T` (as a qualified access expression), we should not regard it as an expression here.
val secondLast = context.qualifiedAccessOrAnnotationCalls.elementAtOrNull(context.qualifiedAccessOrAnnotationCalls.size - 2)
val secondLast = context.qualifiedAccessOrAssignmentsOrAnnotationCalls.elementAtOrNull(context.qualifiedAccessOrAssignmentsOrAnnotationCalls.size - 2)
if (secondLast is FirQualifiedAccessExpression && secondLast.explicitReceiver == expression) return
val diagnostic = expression.typeRef.coneTypeParameterInQualifiedAccess ?: return
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.fir.expressions.*
object FirUnsupportedArrayLiteralChecker : FirArrayOfCallChecker() {
override fun check(expression: FirArrayOfCall, context: CheckerContext, reporter: DiagnosticReporter) {
if (!isInsideAnnotationCall(expression, context) &&
(context.qualifiedAccessOrAnnotationCalls.isNotEmpty() || !isInsideAnnotationClass(context))
(context.qualifiedAccessOrAssignmentsOrAnnotationCalls.isNotEmpty() || !isInsideAnnotationClass(context))
) {
reporter.reportOn(
expression.source,
@@ -32,7 +32,7 @@ object FirUnsupportedArrayLiteralChecker : FirArrayOfCallChecker() {
}
private fun isInsideAnnotationCall(expression: FirArrayOfCall, context: CheckerContext): Boolean {
context.qualifiedAccessOrAnnotationCalls.lastOrNull()?.let {
context.qualifiedAccessOrAssignmentsOrAnnotationCalls.lastOrNull()?.let {
val arguments = when (it) {
is FirFunctionCall ->
if (it.typeRef.toRegularClassSymbol(context.session)?.classKind == ClassKind.ANNOTATION_CLASS) {
@@ -87,4 +87,4 @@ object FirUnsupportedArrayLiteralChecker : FirArrayOfCallChecker() {
return false
}
}
}
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.dispatchReceiverClassTypeOrNull
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.expressions.calleeReference
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.psi
@@ -32,7 +33,7 @@ import org.jetbrains.kotlin.psi.KtNameReferenceExpression
object CanBeReplacedWithOperatorAssignmentChecker : FirVariableAssignmentChecker() {
override fun check(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) {
val lValue = expression.lValue
val lValue = expression.calleeReference
if (lValue !is FirResolvedNamedReference) return
if (expression.source?.kind is KtFakeSourceElementKind) return
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.getChildren
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfoData
import org.jetbrains.kotlin.fir.expressions.calleeReference
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
@@ -48,7 +49,7 @@ object CanBeValChecker : AbstractFirPropertyInitializationChecker() {
override fun visitNode(node: CFGNode<*>) {}
override fun visitVariableAssignmentNode(node: VariableAssignmentNode) {
val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: return
val symbol = node.fir.calleeReference?.toResolvedPropertySymbol() ?: return
if (symbol.isVar && symbol.source?.kind !is KtFakeSourceElementKind && symbol in data.properties &&
(!symbol.requiresInitialization || data.getValue(node).values.any { it[symbol]?.canBeRevisited() == true })
) {
@@ -44,7 +44,7 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
override fun visitNode(node: CFGNode<*>) {}
override fun visitVariableAssignmentNode(node: VariableAssignmentNode) {
val variableSymbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: return
val variableSymbol = node.fir.calleeReference?.toResolvedPropertySymbol() ?: return
val dataPerNode = data[node] ?: return
for (dataPerLabel in dataPerNode.values) {
val data = dataPerLabel[variableSymbol] ?: continue
@@ -210,7 +210,7 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
data: PathAwareVariableStatusInfo
): PathAwareVariableStatusInfo {
val dataForNode = visitNode(node, data)
val symbol = node.fir.lValue.toResolvedPropertySymbol() ?: return dataForNode
val symbol = node.fir.calleeReference?.toResolvedPropertySymbol() ?: return dataForNode
return update(dataForNode, symbol) update@{ prev ->
val toPut = when {
symbol !in localProperties -> {
@@ -247,7 +247,7 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
annotation: FirAnnotation,
): PathAwareVariableStatusInfo {
return if (annotation is FirAnnotationCall) {
val qualifiedAccesses = annotation.argumentList.arguments.mapNotNull { it as? FirQualifiedAccess }.toTypedArray()
val qualifiedAccesses = annotation.argumentList.arguments.mapNotNull { it as? FirQualifiedAccessExpression }.toTypedArray()
visitQualifiedAccesses(dataForNode, *qualifiedAccesses)
} else {
dataForNode
@@ -256,9 +256,9 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
private fun visitQualifiedAccesses(
dataForNode: PathAwareVariableStatusInfo,
vararg qualifiedAccesses: FirQualifiedAccess,
vararg qualifiedAccesses: FirQualifiedAccessExpression,
): PathAwareVariableStatusInfo {
fun retrieveSymbol(qualifiedAccess: FirQualifiedAccess): FirPropertySymbol? {
fun retrieveSymbol(qualifiedAccess: FirQualifiedAccessExpression): FirPropertySymbol? {
return qualifiedAccess.calleeReference.toResolvedPropertySymbol()?.takeIf { it in localProperties }
}
@@ -36,7 +36,7 @@ object FirOptInUsageTypeRefChecker : FirTypeRefChecker() {
val symbol = coneType.lookupTag.toSymbol(context.session) ?: return
symbol.lazyResolveToPhase(FirResolvePhase.STATUS)
val classId = symbol.classId
val lastAnnotationCall = context.qualifiedAccessOrAnnotationCalls.lastOrNull() as? FirAnnotation
val lastAnnotationCall = context.qualifiedAccessOrAssignmentsOrAnnotationCalls.lastOrNull() as? FirAnnotation
if (lastAnnotationCall == null || lastAnnotationCall.annotationTypeRef !== typeRef) {
if (classId == OptInNames.REQUIRES_OPT_IN_CLASS_ID || classId == OptInNames.OPT_IN_CLASS_ID) {
reporter.reportOn(source, OPT_IN_CAN_ONLY_BE_USED_AS_ANNOTATION, context)
@@ -36,9 +36,13 @@ abstract class AbstractDiagnosticCollectorVisitor(
override fun visitElement(element: FirElement, data: Nothing?) {
if (element is FirAnnotationContainer) {
visitAnnotationContainer(element, data)
withAnnotationContainer(element) {
checkElement(element)
visitNestedElements(element)
}
return
}
checkElement(element)
visitNestedElements(element)
}
@@ -23,10 +23,7 @@ import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedErrorReference
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.types.ConeErrorType
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.renderForDebugging
import org.jetbrains.kotlin.fir.types.*
class ErrorNodeDiagnosticCollectorComponent(
session: FirSession,
@@ -63,13 +60,9 @@ class ErrorNodeDiagnosticCollectorComponent(
private fun processErrorReference(reference: FirNamedReference, diagnostic: ConeDiagnostic, context: CheckerContext) {
val source = reference.source ?: return
val qualifiedAccessOrAnnotationCall = context.qualifiedAccessOrAnnotationCalls.lastOrNull()?.takeIf {
val qualifiedAccessOrAnnotationCall = context.qualifiedAccessOrAssignmentsOrAnnotationCalls.lastOrNull()?.takeIf {
// Use the source of the enclosing FirQualifiedAccess if it is exactly the call to the erroneous callee.
when (it) {
is FirQualifiedAccess -> it.calleeReference == reference
is FirAnnotationCall -> it.calleeReference == reference
else -> false
}
it.calleeReference == reference
}
// Don't report duplicated unresolved reference on annotation entry (already reported on its type)
if (source.elementType == KtNodeTypes.ANNOTATION_ENTRY && diagnostic is ConeUnresolvedNameError) return
@@ -79,7 +72,7 @@ class ErrorNodeDiagnosticCollectorComponent(
) return
// If the receiver cannot be resolved, we skip reporting any further problems for this call.
if (qualifiedAccessOrAnnotationCall is FirQualifiedAccess) {
if (qualifiedAccessOrAnnotationCall is FirQualifiedAccessExpression) {
if (qualifiedAccessOrAnnotationCall.dispatchReceiver.cannotBeResolved() ||
qualifiedAccessOrAnnotationCall.extensionReceiver.cannotBeResolved() ||
qualifiedAccessOrAnnotationCall.explicitReceiver.cannotBeResolved()
@@ -91,10 +91,6 @@ private fun ConeDiagnostic.toKtDiagnostic(
is ConeOperatorAmbiguityError -> FirErrors.ASSIGN_OPERATOR_AMBIGUITY.createOn(source, this.candidateSymbols)
is ConeVariableExpectedError -> FirErrors.VARIABLE_EXPECTED.createOn(source)
is ConeValReassignmentError -> when (val symbol = this.variable) {
is FirBackingFieldSymbol -> FirErrors.VAL_REASSIGNMENT_VIA_BACKING_FIELD.errorFactory.createOn(source, symbol)
else -> FirErrors.VAL_REASSIGNMENT.createOn(source, symbol)
}
is ConeUnexpectedTypeArgumentsError -> FirErrors.TYPE_ARGUMENTS_NOT_ALLOWED.createOn(this.source ?: source)
is ConeIllegalAnnotationError -> FirErrors.NOT_AN_ANNOTATION_CLASS.createOn(source, this.name.asString())