[FIR] Replace getContainingClass usages with getContainingClassSymbol

This commit is contained in:
Dmitriy Novozhilov
2021-07-07 15:22:23 +03:00
committed by teamcityserver
parent 48a25e2fe6
commit c99a02796f
10 changed files with 84 additions and 96 deletions
@@ -242,7 +242,7 @@ internal object ConeTypeCompatibilityChecker {
private fun FirTypeParameterSymbol?.collectUpperBounds(): Set<ConeClassLikeType> { private fun FirTypeParameterSymbol?.collectUpperBounds(): Set<ConeClassLikeType> {
if (this == null) return emptySet() if (this == null) return emptySet()
return fir.bounds.flatMap { it.coneTypeSafe<ConeKotlinType>().collectUpperBounds() }.toSet() return resolvedBounds.flatMap { it.coneTypeSafe<ConeKotlinType>().collectUpperBounds() }.toSet()
} }
private fun ConeKotlinType?.collectLowerBounds(): Set<ConeClassLikeType> { private fun ConeKotlinType?.collectLowerBounds(): Set<ConeClassLikeType> {
@@ -17,17 +17,18 @@ import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.* import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.SessionHolder import org.jetbrains.kotlin.fir.resolve.SessionHolder
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType
import org.jetbrains.kotlin.fir.resolve.symbolProvider import org.jetbrains.kotlin.fir.resolve.symbolProvider
import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.impl.multipleDelegatesWithTheSameSignature import org.jetbrains.kotlin.fir.scopes.impl.multipleDelegatesWithTheSameSignature
import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
@@ -47,9 +48,9 @@ import org.jetbrains.kotlin.util.ImplementationStatus
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
private val INLINE_ONLY_ANNOTATION_CLASS_ID = ClassId.topLevel(FqName("kotlin.internal.InlineOnly")) private val INLINE_ONLY_ANNOTATION_CLASS_ID: ClassId = ClassId.topLevel(FqName("kotlin.internal.InlineOnly"))
fun FirClass.unsubstitutedScope(context: CheckerContext) = fun FirClass.unsubstitutedScope(context: CheckerContext): FirTypeScope =
this.unsubstitutedScope(context.sessionHolder.session, context.sessionHolder.scopeSession, withForcedTypeCalculator = false) this.unsubstitutedScope(context.sessionHolder.session, context.sessionHolder.scopeSession, withForcedTypeCalculator = false)
/** /**
@@ -121,32 +122,21 @@ fun FirTypeRef.toRegularClass(session: FirSession): FirRegularClass? {
return coneType.toRegularClass(session) return coneType.toRegularClass(session)
} }
/**
* Returns FirSimpleFunction based on the given FirFunctionCall
*/
inline fun <reified T : Any> FirQualifiedAccessExpression.getDeclaration(): T? {
return this.calleeReference.safeAs<FirResolvedNamedReference>()
?.resolvedSymbol
?.fir as? T
}
/** /**
* Returns the ClassLikeDeclaration where the Fir object has been defined * Returns the ClassLikeDeclaration where the Fir object has been defined
* or null if no proper declaration has been found. * or null if no proper declaration has been found.
*/ */
fun FirDeclaration.getContainingClass(context: CheckerContext): FirClassLikeDeclaration? = fun FirDeclaration.getContainingClassSymbol(context: CheckerContext): FirClassLikeSymbol<*>? =
this.safeAs<FirCallableDeclaration>()?.containingClass()?.toSymbol(context.session)?.fir this.safeAs<FirCallableDeclaration>()?.containingClass()?.toSymbol(context.session)
fun FirClassLikeSymbol<*>.outerClass(context: CheckerContext): FirClassLikeSymbol<*>? { fun FirBasedSymbol<*>.getContainingClassSymbol(context: CheckerContext): FirClassLikeSymbol<*>? = fir.getContainingClassSymbol(context)
fun FirClassLikeSymbol<*>.outerClassSymbol(context: CheckerContext): FirClassLikeSymbol<*>? {
if (this !is FirClassSymbol<*>) return null if (this !is FirClassSymbol<*>) return null
val outerClassId = classId.outerClassId ?: return null val outerClassId = classId.outerClassId ?: return null
return context.session.symbolProvider.getClassLikeSymbolByFqName(outerClassId) return context.session.symbolProvider.getClassLikeSymbolByFqName(outerClassId)
} }
fun FirClass.outerClass(context: CheckerContext): FirClass? {
return symbol.outerClass(context)?.fir as? FirClass
}
/** /**
* Returns the FirClassLikeDeclaration that the * Returns the FirClassLikeDeclaration that the
* sequence of FirTypeAlias'es points to starting * sequence of FirTypeAlias'es points to starting
@@ -290,10 +280,10 @@ fun FirClass.findNonInterfaceSupertype(context: CheckerContext): FirTypeRef? {
return null return null
} }
val FirFunctionCall.isIterator val FirFunctionCall.isIterator: Boolean
get() = this.calleeReference.name.asString() == "<iterator>" get() = this.calleeReference.name.asString() == "<iterator>"
fun ConeKotlinType.isSubtypeOfThrowable(session: FirSession) = fun ConeKotlinType.isSubtypeOfThrowable(session: FirSession): Boolean =
session.builtinTypes.throwableType.type.isSupertypeOf(session.typeContext, this.fullyExpandedType(session)) session.builtinTypes.throwableType.type.isSupertypeOf(session.typeContext, this.fullyExpandedType(session))
val FirValueParameter.hasValOrVar: Boolean val FirValueParameter.hasValOrVar: Boolean
@@ -302,10 +292,10 @@ val FirValueParameter.hasValOrVar: Boolean
return source.getChild(VAL_VAR_TOKEN_SET) != null return source.getChild(VAL_VAR_TOKEN_SET) != null
} }
fun KotlinTypeMarker.isSupertypeOf(context: TypeCheckerProviderContext, type: KotlinTypeMarker?) = fun KotlinTypeMarker.isSupertypeOf(context: TypeCheckerProviderContext, type: KotlinTypeMarker?): Boolean =
type != null && AbstractTypeChecker.isSubtypeOf(context, type, this) type != null && AbstractTypeChecker.isSubtypeOf(context, type, this)
fun KotlinTypeMarker.isSubtypeOf(context: TypeCheckerProviderContext, type: KotlinTypeMarker?) = fun KotlinTypeMarker.isSubtypeOf(context: TypeCheckerProviderContext, type: KotlinTypeMarker?): Boolean =
type != null && AbstractTypeChecker.isSubtypeOf(context, this, type) type != null && AbstractTypeChecker.isSubtypeOf(context, this, type)
fun ConeKotlinType.canHaveSubtypes(session: FirSession): Boolean { fun ConeKotlinType.canHaveSubtypes(session: FirSession): Boolean {
@@ -380,7 +370,8 @@ private fun lowerThanBound(context: ConeInferenceContext, argument: ConeKotlinTy
return false return false
} }
fun FirMemberDeclaration.isInlineOnly(): Boolean = isInline && (this as FirAnnotatedDeclaration).hasAnnotation(INLINE_ONLY_ANNOTATION_CLASS_ID) fun FirMemberDeclaration.isInlineOnly(): Boolean =
isInline && (this as FirAnnotatedDeclaration).hasAnnotation(INLINE_ONLY_ANNOTATION_CLASS_ID)
fun isSubtypeForTypeMismatch(context: ConeInferenceContext, subtype: ConeKotlinType, supertype: ConeKotlinType): Boolean { fun isSubtypeForTypeMismatch(context: ConeInferenceContext, subtype: ConeKotlinType, supertype: ConeKotlinType): Boolean {
val subtypeFullyExpanded = subtype.fullyExpandedType(context.session) val subtypeFullyExpanded = subtype.fullyExpandedType(context.session)
@@ -6,8 +6,8 @@
package org.jetbrains.kotlin.fir.analysis.checkers.declaration package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClass
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_ABSTRACT_METHOD_WITH_DEFAULT_VALUE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_ABSTRACT_METHOD_WITH_DEFAULT_VALUE
@@ -31,6 +31,7 @@ object FirFunInterfaceDeclarationChecker : FirRegularClassChecker() {
if (!declaration.isInterface || !declaration.isFun) return if (!declaration.isInterface || !declaration.isFun) return
val scope = declaration.unsubstitutedScope(context) val scope = declaration.unsubstitutedScope(context)
val classSymbol = declaration.symbol
var abstractFunction: FirSimpleFunction? = null var abstractFunction: FirSimpleFunction? = null
@@ -52,7 +53,7 @@ object FirFunInterfaceDeclarationChecker : FirRegularClassChecker() {
val firProperty = property.fir as? FirProperty ?: continue val firProperty = property.fir as? FirProperty ?: continue
if (firProperty.isAbstract) { if (firProperty.isAbstract) {
val source = val source =
if (firProperty.getContainingClass(context) != declaration) if (firProperty.getContainingClassSymbol(context) != classSymbol)
declaration.source declaration.source
else else
firProperty.source firProperty.source
@@ -67,7 +68,7 @@ object FirFunInterfaceDeclarationChecker : FirRegularClassChecker() {
return return
} }
val inFunInterface = abstractFunction.getContainingClass(context) === declaration val inFunInterface = abstractFunction.getContainingClassSymbol(context) === classSymbol
when { when {
abstractFunction.typeParameters.isNotEmpty() -> abstractFunction.typeParameters.isNotEmpty() ->
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.delegatedWrapperData
import org.jetbrains.kotlin.fir.scopes.impl.multipleDelegatesWithTheSameSignature import org.jetbrains.kotlin.fir.scopes.impl.multipleDelegatesWithTheSameSignature
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.unwrapFakeOverrides import org.jetbrains.kotlin.fir.unwrapFakeOverrides
import org.jetbrains.kotlin.util.ImplementationStatus import org.jetbrains.kotlin.util.ImplementationStatus
@@ -166,5 +167,5 @@ object FirNotImplementedOverrideChecker : FirClassChecker() {
} }
private fun FirCallableDeclaration.isFromInterfaceOrEnum(context: CheckerContext): Boolean = private fun FirCallableDeclaration.isFromInterfaceOrEnum(context: CheckerContext): Boolean =
(getContainingClass(context) as? FirRegularClass)?.let { it.isInterface || it.isEnumClass } == true (getContainingClassSymbol(context) as? FirRegularClassSymbol)?.let { it.isInterface || it.isEnumClass } == true
} }
@@ -9,16 +9,17 @@ import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext 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.context.findClosest
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClass
import org.jetbrains.kotlin.fir.analysis.checkers.getDeclaration
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.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.containingClass
import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.utils.modality import org.jetbrains.kotlin.fir.declarations.utils.isAbstract
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
object FirAbstractSuperCallChecker : FirQualifiedAccessExpressionChecker() { object FirAbstractSuperCallChecker : FirQualifiedAccessExpressionChecker() {
@@ -33,16 +34,11 @@ object FirAbstractSuperCallChecker : FirQualifiedAccessExpressionChecker() {
if (closestClass.classKind == ClassKind.CLASS) { if (closestClass.classKind == ClassKind.CLASS) {
// handles all the FirSimpleFunction/FirProperty/etc. // handles all the FirSimpleFunction/FirProperty/etc.
val item = expression.getDeclaration<FirCallableDeclaration>() val declarationSymbol = expression.toResolvedCallableSymbol() ?: return
?: return
val declaration = item.getContainingClass(context).safeAs<FirRegularClass>() val containingClassSymbol = declarationSymbol.containingClass()?.toSymbol(context.session) as? FirRegularClassSymbol ?: return
?: return
if ( if (containingClassSymbol.isAbstract && declarationSymbol.isAbstract) {
declaration.modality == Modality.ABSTRACT &&
item.modality == Modality.ABSTRACT
) {
reporter.reportOn(expression.calleeReference.source, FirErrors.ABSTRACT_SUPER_CALL, context) reporter.reportOn(expression.calleeReference.source, FirErrors.ABSTRACT_SUPER_CALL, context)
} }
} }
@@ -8,14 +8,13 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext 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.context.findClosest
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClass import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
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.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.fir.references.FirSuperReference
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -29,22 +28,12 @@ object FirSuperclassNotAccessibleFromInterfaceChecker : FirQualifiedAccessExpres
val closestClass = context.findClosest<FirRegularClass>() ?: return val closestClass = context.findClosest<FirRegularClass>() ?: return
if (closestClass.classKind == ClassKind.INTERFACE) { if (closestClass.classKind == ClassKind.INTERFACE) {
val origin = getClassLikeDeclaration(expression, context) val containingClassSymbol =
?.symbol.safeAs<FirRegularClassSymbol>() expression.toResolvedCallableSymbol()?.getContainingClassSymbol(context) as? FirRegularClassSymbol ?: return
?.fir
?: return
if (origin.source != null && origin.classKind == ClassKind.CLASS) { if (containingClassSymbol.classKind == ClassKind.CLASS) {
reporter.reportOn(expression.explicitReceiver?.source, FirErrors.SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE, context) reporter.reportOn(expression.explicitReceiver?.source, FirErrors.SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE, context)
} }
} }
} }
/**
* Returns the ClassLikeDeclaration where the function has been defined
* or null if no proper declaration has been found.
*/
private fun getClassLikeDeclaration(functionCall: FirQualifiedAccessExpression, context: CheckerContext): FirClassLikeDeclaration? {
return functionCall.calleeReference.safeAs<FirResolvedNamedReference>()?.resolvedSymbol?.fir?.getContainingClass(context)
}
} }
@@ -8,8 +8,8 @@ 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.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isEnumEntryInitializer import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isEnumEntryInitializer
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClass import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
import org.jetbrains.kotlin.fir.analysis.checkers.outerClass import org.jetbrains.kotlin.fir.analysis.checkers.outerClassSymbol
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
@@ -18,6 +18,10 @@ import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.FirEnumEntrySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
object FirUninitializedEnumChecker : FirQualifiedAccessExpressionChecker() { object FirUninitializedEnumChecker : FirQualifiedAccessExpressionChecker() {
// Initialization order: member property initializers, enum entries, companion object (including members in it). // Initialization order: member property initializers, enum entries, companion object (including members in it).
@@ -68,17 +72,17 @@ object FirUninitializedEnumChecker : FirQualifiedAccessExpressionChecker() {
if (source.kind is FirFakeSourceElementKind) return if (source.kind is FirFakeSourceElementKind) return
val reference = expression.calleeReference as? FirResolvedNamedReference ?: return val reference = expression.calleeReference as? FirResolvedNamedReference ?: return
val calleeDeclaration = reference.resolvedSymbol.fir val calleeSymbol = reference.resolvedSymbol
val calleeContainingClass = calleeDeclaration.getContainingClass(context) as? FirRegularClass ?: return val calleeContainingClassSymbol = calleeSymbol.getContainingClassSymbol(context) as? FirRegularClassSymbol ?: return
// We're looking for members/entries/companion object in an enum class or members in companion object of an enum class. // We're looking for members/entries/companion object in an enum class or members in companion object of an enum class.
val calleeIsInsideEnum = calleeContainingClass.isEnumClass val calleeIsInsideEnum = calleeContainingClassSymbol.isEnumClass
val calleeIsInsideEnumCompanion = val calleeIsInsideEnumCompanion =
calleeContainingClass.isCompanion && (calleeContainingClass.outerClass(context) as? FirRegularClass)?.isEnumClass == true calleeContainingClassSymbol.isCompanion && (calleeContainingClassSymbol.outerClassSymbol(context) as? FirRegularClassSymbol)?.isEnumClass == true
if (!calleeIsInsideEnum && !calleeIsInsideEnumCompanion) return if (!calleeIsInsideEnum && !calleeIsInsideEnumCompanion) return
val enumClass = val enumClassSymbol =
if (calleeIsInsideEnum) calleeContainingClass if (calleeIsInsideEnum) calleeContainingClassSymbol
else calleeContainingClass.outerClass(context) as? FirRegularClass ?: return else calleeContainingClassSymbol.outerClassSymbol(context) as? FirRegularClassSymbol ?: return
// An accessed context within the enum class of interest. We should look up until either enum members or enum entries are found, // An accessed context within the enum class of interest. We should look up until either enum members or enum entries are found,
// not just last containing declaration. For example, // not just last containing declaration. For example,
@@ -99,11 +103,11 @@ object FirUninitializedEnumChecker : FirQualifiedAccessExpressionChecker() {
// INSTANCE(EnumCompanion2.foo()) // INSTANCE(EnumCompanion2.foo())
// } // }
// find an accessed context within the same enum class. // find an accessed context within the same enum class.
it.getContainingClass(context) == enumClass it.getContainingClassSymbol(context) == enumClassSymbol
} ?: return }?.symbol ?: return
val enumMemberProperties = enumClass.declarations.filterIsInstance<FirProperty>() val enumMemberProperties = enumClassSymbol.declarationSymbols.filterIsInstance<FirPropertySymbol>()
val enumEntries = enumClass.declarations.filterIsInstance<FirEnumEntry>() val enumEntries = enumClassSymbol.declarationSymbols.filterIsInstance<FirEnumEntrySymbol>()
// When checking enum member properties, accesses to enum entries in lazy delegation is legitimate, e.g., // When checking enum member properties, accesses to enum entries in lazy delegation is legitimate, e.g.,
// enum JvmTarget(...) { // enum JvmTarget(...) {
@@ -114,7 +118,7 @@ object FirUninitializedEnumChecker : FirQualifiedAccessExpressionChecker() {
// } // }
// } // }
if (accessedContext in enumMemberProperties) { if (accessedContext in enumMemberProperties) {
val lazyDelegation = (accessedContext as FirProperty).lazyDelegation val lazyDelegation = (accessedContext as FirPropertySymbol).lazyDelegation
if (lazyDelegation != null && lazyDelegation == context.containingDeclarations.lastOrNull()) { if (lazyDelegation != null && lazyDelegation == context.containingDeclarations.lastOrNull()) {
return return
} }
@@ -135,10 +139,10 @@ object FirUninitializedEnumChecker : FirQualifiedAccessExpressionChecker() {
} }
// Members inside the companion object of an enum class // Members inside the companion object of an enum class
if (calleeContainingClass == enumClass.companionObject) { if (calleeContainingClassSymbol == enumClassSymbol.companionObjectSymbol) {
// Uninitialized from the point of view of members or enum entries of that enum class // Uninitialized from the point of view of members or enum entries of that enum class
if (accessedContext in enumMemberProperties || accessedContext in enumEntries) { if (accessedContext in enumMemberProperties || accessedContext in enumEntries) {
if (calleeDeclaration is FirProperty) { if (calleeSymbol is FirProperty) {
// From KT-11769 // From KT-11769
// enum class Fruit(...) { // enum class Fruit(...) {
// APPLE(...); // APPLE(...);
@@ -147,7 +151,7 @@ object FirUninitializedEnumChecker : FirQualifiedAccessExpressionChecker() {
// } // }
// val score = ... <!>common<!> // val score = ... <!>common<!>
// } // }
reporter.reportOn(source, FirErrors.UNINITIALIZED_VARIABLE, calleeDeclaration.symbol, context) reporter.reportOn(source, FirErrors.UNINITIALIZED_VARIABLE, calleeSymbol.symbol, context)
} else { } else {
// enum class EnumCompanion2(...) { // enum class EnumCompanion2(...) {
// INSTANCE(<!>foo<!>()) // INSTANCE(<!>foo<!>())
@@ -156,18 +160,18 @@ object FirUninitializedEnumChecker : FirQualifiedAccessExpressionChecker() {
// } // }
// } // }
// <!>Companion<!>.foo() v.s. <!>foo<!>() // <!>Companion<!>.foo() v.s. <!>foo<!>()
if ((expression.explicitReceiver as? FirResolvedQualifier)?.symbol?.fir == enumClass.companionObject) { if ((expression.explicitReceiver as? FirResolvedQualifier)?.symbol == enumClassSymbol.companionObjectSymbol) {
reporter.reportOn( reporter.reportOn(
expression.explicitReceiver!!.source, expression.explicitReceiver!!.source,
FirErrors.UNINITIALIZED_ENUM_COMPANION, FirErrors.UNINITIALIZED_ENUM_COMPANION,
enumClass.symbol, enumClassSymbol,
context context
) )
} else { } else {
reporter.reportOn( reporter.reportOn(
expression.calleeReference.source, expression.calleeReference.source,
FirErrors.UNINITIALIZED_ENUM_COMPANION, FirErrors.UNINITIALIZED_ENUM_COMPANION,
enumClass.symbol, enumClassSymbol,
context context
) )
} }
@@ -176,8 +180,8 @@ object FirUninitializedEnumChecker : FirQualifiedAccessExpressionChecker() {
} }
// The enum entries of an enum class // The enum entries of an enum class
if (calleeDeclaration in enumEntries) { if (calleeSymbol in enumEntries) {
val calleeEnumEntry = calleeDeclaration as FirEnumEntry val calleeEnumEntry = calleeSymbol as FirEnumEntrySymbol
// Uninitialized from the point of view of members of that enum class // Uninitialized from the point of view of members of that enum class
if (accessedContext in enumMemberProperties) { if (accessedContext in enumMemberProperties) {
// From KT-6054 // From KT-6054
@@ -188,7 +192,7 @@ object FirUninitializedEnumChecker : FirQualifiedAccessExpressionChecker() {
// <!>B<!> -> ... // <!>B<!> -> ...
// } // }
// } // }
reporter.reportOn(source, FirErrors.UNINITIALIZED_ENUM_ENTRY, calleeEnumEntry.symbol, context) reporter.reportOn(source, FirErrors.UNINITIALIZED_ENUM_ENTRY, calleeEnumEntry, context)
} }
// enum class A(...) { // enum class A(...) {
// A1(<!>A2<!>), // A1(<!>A2<!>),
@@ -198,7 +202,7 @@ object FirUninitializedEnumChecker : FirQualifiedAccessExpressionChecker() {
if (accessedContext in enumEntries) { if (accessedContext in enumEntries) {
// Technically, this is equal to `enumEntries.indexOf(accessedContext) <= enumEntries.indexOf(calleeDeclaration)`. // Technically, this is equal to `enumEntries.indexOf(accessedContext) <= enumEntries.indexOf(calleeDeclaration)`.
// Instead of double `indexOf`, we can iterate entries just once until either one appears. // Instead of double `indexOf`, we can iterate entries just once until either one appears.
var precedingEntry: FirEnumEntry? = null var precedingEntry: FirEnumEntrySymbol? = null
enumEntries.forEach { enumEntries.forEach {
if (precedingEntry != null) return@forEach if (precedingEntry != null) return@forEach
if (it == calleeEnumEntry || it == accessedContext) { if (it == calleeEnumEntry || it == accessedContext) {
@@ -206,16 +210,17 @@ object FirUninitializedEnumChecker : FirQualifiedAccessExpressionChecker() {
} }
} }
if (precedingEntry == accessedContext) { if (precedingEntry == accessedContext) {
reporter.reportOn(source, FirErrors.UNINITIALIZED_ENUM_ENTRY, calleeEnumEntry.symbol, context) reporter.reportOn(source, FirErrors.UNINITIALIZED_ENUM_ENTRY, calleeEnumEntry, context)
} }
} }
} }
} }
private val FirProperty.lazyDelegation: FirAnonymousFunction? private val FirPropertySymbol.lazyDelegation: FirAnonymousFunction?
get() { get() {
if (delegate == null || delegate !is FirFunctionCall) return null ensureResolved(FirResolvePhase.BODY_RESOLVE)
val delegateCall = delegate as FirFunctionCall if (fir.delegate == null || fir.delegate !is FirFunctionCall) return null
val delegateCall = fir.delegate as FirFunctionCall
val callee = val callee =
(delegateCall.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol?.fir as? FirSimpleFunction ?: return null (delegateCall.calleeReference as? FirResolvedNamedReference)?.resolvedSymbol?.fir as? FirSimpleFunction ?: return null
if (callee.symbol.callableId.asSingleFqName().asString() != "kotlin.lazy") return null if (callee.symbol.callableId.asSingleFqName().asString() != "kotlin.lazy") return null
@@ -5,22 +5,26 @@
package org.jetbrains.kotlin.fir.analysis.checkers.extended package org.jetbrains.kotlin.fir.analysis.checkers.extended
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.FirFakeSourceElement import org.jetbrains.kotlin.fir.FirFakeSourceElement
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext 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.context.findClosest
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember
import org.jetbrains.kotlin.fir.analysis.checkers.findClosestClassOrObject
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
import org.jetbrains.kotlin.fir.analysis.checkers.overriddenFunctions
import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirDeclarationSyntaxChecker import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirDeclarationSyntaxChecker
import org.jetbrains.kotlin.fir.analysis.checkers.toVisibilityOrNull
import org.jetbrains.kotlin.fir.analysis.diagnostics.* import org.jetbrains.kotlin.fir.analysis.diagnostics.*
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
import org.jetbrains.kotlin.fir.declarations.utils.isOverride import org.jetbrains.kotlin.fir.declarations.utils.isOverride
import org.jetbrains.kotlin.fir.declarations.utils.isSealed
import org.jetbrains.kotlin.fir.declarations.utils.visibility import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
@@ -71,10 +75,10 @@ object RedundantVisibilityModifierSyntaxChecker : FirDeclarationSyntaxChecker<Fi
} }
this is FirConstructor -> { this is FirConstructor -> {
val clazz = this.getContainingClass(context) val classSymbol = this.getContainingClassSymbol(context)
if ( if (
clazz is FirClass classSymbol is FirRegularClassSymbol
&& (clazz.classKind == ClassKind.ENUM_CLASS || clazz.modality() == Modality.SEALED) && (classSymbol.isEnumClass || classSymbol.isSealed)
) { ) {
Visibilities.Private Visibilities.Private
} else { } else {
@@ -14,11 +14,12 @@ import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.cfa.* import org.jetbrains.kotlin.fir.analysis.cfa.*
import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClass import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
import org.jetbrains.kotlin.fir.analysis.checkers.isIterator import org.jetbrains.kotlin.fir.analysis.checkers.isIterator
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.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
@@ -31,10 +32,7 @@ import org.jetbrains.kotlin.fir.types.coneType
object UnusedChecker : FirControlFlowChecker() { object UnusedChecker : FirControlFlowChecker() {
override fun analyze(graph: ControlFlowGraph, reporter: DiagnosticReporter, context: CheckerContext) { override fun analyze(graph: ControlFlowGraph, reporter: DiagnosticReporter, context: CheckerContext) {
if (graph.declaration?.getContainingClass(context)?.takeIf { if (graph.declaration?.getContainingClassSymbol(context)?.takeIf { !it.isLocal } != null) return
!it.symbol.classId.isLocal
} != null
) return
val (properties, _) = LocalPropertyAndCapturedWriteCollector.collect(graph) val (properties, _) = LocalPropertyAndCapturedWriteCollector.collect(graph)
if (properties.isEmpty()) return if (properties.isEmpty()) return
@@ -57,6 +57,9 @@ class FirRegularClassSymbol(classId: ClassId) : FirClassSymbol<FirRegularClass>(
ensureResolved(FirResolvePhase.STATUS) ensureResolved(FirResolvePhase.STATUS)
return fir.status as FirResolvedDeclarationStatus return fir.status as FirResolvedDeclarationStatus
} }
val companionObjectSymbol: FirRegularClassSymbol?
get() = fir.companionObject?.symbol
} }
val ANONYMOUS_CLASS_ID = ClassId(FqName.ROOT, FqName.topLevel(Name.special("<anonymous>")), true) val ANONYMOUS_CLASS_ID = ClassId(FqName.ROOT, FqName.topLevel(Name.special("<anonymous>")), true)