org.jetbrains.kotlin.fir.analysis.checkers.FirHelpers.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.analysis.checkers
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSymbolOwner
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtModifierList
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
/**
* Returns true if this is a superclass of other.
*/
fun FirClass<*>.isSuperclassOf(other: FirClass<*>): Boolean {
/**
* Hides additional parameters.
*/
fun FirClass<*>.isSuperclassOf(other: FirClass<*>, exclude: MutableSet>): Boolean {
for (it in other.superTypeRefs) {
val that = it.firClassLike(session)
?.followAllAlias(session)
?.safeAs>()
?: continue
if (that in exclude) {
continue
}
if (that.classKind == ClassKind.CLASS) {
if (that == this) {
return true
}
exclude.add(that)
return this.isSuperclassOf(that, exclude)
}
}
return false
}
return isSuperclassOf(other, mutableSetOf())
}
/**
* Returns true if this is a supertype of other.
*/
fun FirClass<*>.isSupertypeOf(other: FirClass<*>): Boolean {
/**
* Hides additional parameters.
*/
fun FirClass<*>.isSupertypeOf(other: FirClass<*>, exclude: MutableSet>): Boolean {
for (it in other.superTypeRefs) {
val candidate = it.firClassLike(session)
?.followAllAlias(session)
?.safeAs>()
?: continue
if (candidate in exclude) {
continue
}
exclude.add(candidate)
if (candidate == this) {
return true
}
if (this.isSupertypeOf(candidate, exclude)) {
return true
}
}
return false
}
return isSupertypeOf(other, mutableSetOf())
}
/**
* Returns the FirRegularClass associated with this
* or null of something goes wrong.
*/
fun ConeClassLikeType.toRegularClass(session: FirSession): FirRegularClass? {
return lookupTag.toSymbol(session).safeAs()?.fir
}
/**
* Returns the FirRegularClass associated with this
* or null of something goes wrong.
*/
fun ConeKotlinType.toRegularClass(session: FirSession): FirRegularClass? {
return safeAs()?.toRegularClass(session)
}
/**
* Returns the FirRegularClass associated with this
* or null of something goes wrong.
*/
fun FirTypeRef.toRegularClass(session: FirSession): FirRegularClass? {
return safeAs()?.type?.toRegularClass(session)
}
/**
* Returns FirSimpleFunction based on the given FirFunctionCall
*/
inline fun FirQualifiedAccessExpression.getDeclaration(): T? {
return this.calleeReference.safeAs()
?.resolvedSymbol
?.fir.safeAs()
}
/**
* Returns the ClassLikeDeclaration where the Fir object has been defined
* or null if no proper declaration has been found.
*/
fun FirSymbolOwner<*>.getContainingClass(context: CheckerContext): FirClassLikeDeclaration<*>? {
val classId = this.symbol.safeAs>()
?.callableId
?.classId
?: return null
if (!classId.isLocal) {
return context.session.firSymbolProvider.getClassLikeSymbolByFqName(classId)?.fir
}
return null
}
/**
* Returns the FirClassLikeDeclaration the type alias is pointing
* to provided `this` is a FirTypeAlias. Returns this otherwise.
*/
fun FirClassLikeDeclaration<*>.followAlias(session: FirSession): FirClassLikeDeclaration<*>? {
return this.safeAs()
?.expandedTypeRef
?.firClassLike(session)
?: return this
}
/**
* Returns the FirClassLikeDeclaration that the
* sequence of FirTypeAlias'es points to starting
* with `this`. Or null if something goes wrong.
*/
fun FirClassLikeDeclaration<*>.followAllAlias(session: FirSession): FirClassLikeDeclaration<*>? {
var it: FirClassLikeDeclaration<*>? = this
while (it is FirTypeAlias) {
it = it.expandedTypeRef.firClassLike(session)
}
return it
}
/**
* Returns the closest to the end of context.containingDeclarations
* item like FirRegularClass or FirAnonymousObject
* or null if no such item could be found.
*/
fun CheckerContext.findClosestClassOrObject(): FirClass<*>? {
for (it in containingDeclarations.asReversed()) {
if (
it is FirRegularClass ||
it is FirAnonymousObject
) {
return it as FirClass<*>
}
}
return null
}
/**
* Returns the list of functions that overridden by given
*/
fun FirSimpleFunction.overriddenFunctions(
containingClass: FirClass<*>,
context: CheckerContext
): List> {
val firTypeScope = containingClass.unsubstitutedScope(
context.sessionHolder.session,
context.sessionHolder.scopeSession
)
val overriddenFunctions = mutableListOf>()
firTypeScope.processFunctionsByName(symbol.fir.name) { }
firTypeScope.processOverriddenFunctions(symbol) {
overriddenFunctions.add(it)
ProcessorAction.NEXT
}
return overriddenFunctions
}
/**
* Returns the visibility by given KtModifierList
*/
fun KtModifierList?.getVisibility() = this?.visibilityModifierType()?.toVisibilityOrNull()
/**
* Returns Visibility by token or null
*/
fun KtModifierKeywordToken.toVisibilityOrNull(): Visibility? {
return when (this) {
KtTokens.PUBLIC_KEYWORD -> Visibilities.Public
KtTokens.PRIVATE_KEYWORD -> Visibilities.Private
KtTokens.PROTECTED_KEYWORD -> Visibilities.Protected
KtTokens.INTERNAL_KEYWORD -> Visibilities.Internal
else -> null
}
}
/**
* Returns the modality of the class
*/
fun FirClass<*>.modality(): Modality? {
return when (this) {
is FirRegularClass -> modality
else -> Modality.FINAL
}
}
/**
* returns implicit modality by FirMemberDeclaration
*/
fun FirMemberDeclaration.implicitModality(context: CheckerContext): Modality {
if (this is FirRegularClass && (this.classKind == ClassKind.CLASS || this.classKind == ClassKind.OBJECT)) {
if (this.classKind == ClassKind.INTERFACE) return Modality.ABSTRACT
return Modality.FINAL
}
val klass = context.findClosestClassOrObject() ?: return Modality.FINAL
val modifiers = this.modifierListOrNull() ?: return Modality.FINAL
if (modifiers.contains(KtTokens.OVERRIDE_KEYWORD)) {
val klassModifiers = klass.modifierListOrNull()
if (klassModifiers != null && klassModifiers.run {
contains(KtTokens.ABSTRACT_KEYWORD) || contains(KtTokens.OPEN_KEYWORD) || contains(KtTokens.SEALED_KEYWORD)
}) {
return Modality.OPEN
}
}
if (
klass is FirRegularClass
&& klass.classKind == ClassKind.INTERFACE
&& !modifiers.contains(KtTokens.PRIVATE_KEYWORD)
) {
return if (this.hasBody()) Modality.OPEN else Modality.ABSTRACT
}
return Modality.FINAL
}
private fun FirDeclaration.modifierListOrNull() = this.source.getModifierList()?.modifiers?.map { it.token }
private fun FirDeclaration.hasBody(): Boolean = when (this) {
is FirSimpleFunction -> this.body != null && this.body !is FirEmptyExpressionBlock
is FirProperty -> this.setter?.body !is FirEmptyExpressionBlock? || this.getter?.body !is FirEmptyExpressionBlock?
else -> false
}
/**
* Finds any non-interface supertype and returns it
* or null if couldn't find any.
*/
fun FirClass<*>.findNonInterfaceSupertype(context: CheckerContext): FirTypeRef? {
for (it in superTypeRefs) {
val classId = it.safeAs()
?.type.safeAs()
?.lookupTag?.classId
?: continue
val fir = context.session.firSymbolProvider.getClassLikeSymbolByFqName(classId)
?.fir.safeAs>()
?: continue
if (fir.classKind != ClassKind.INTERFACE) {
return it
}
}
return null
}
/**
* Returns KtModifierToken by Modality
*/
fun Modality.toToken(): KtModifierKeywordToken = when (this) {
Modality.FINAL -> KtTokens.FINAL_KEYWORD
Modality.SEALED -> KtTokens.SEALED_KEYWORD
Modality.OPEN -> KtTokens.OPEN_KEYWORD
Modality.ABSTRACT -> KtTokens.ABSTRACT_KEYWORD
}
val FirFunctionCall.isIterator
get() = this.calleeReference.name.asString() == ""