[FIR] Stop using .fir when checking conflicts

Use the symbols API in the declarations presenter and
conflicts helpers.
This commit is contained in:
Nikolay Lunyak
2023-09-06 09:38:34 +03:00
committed by Space Team
parent 942bff8a03
commit a1cb6258bc
5 changed files with 144 additions and 149 deletions
@@ -18,7 +18,6 @@ import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl.Companion.DEFAULT_STATUS_FOR_STATUSLESS_DECLARATIONS import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl.Companion.DEFAULT_STATUS_FOR_STATUSLESS_DECLARATIONS
import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl.Companion.DEFAULT_STATUS_FOR_SUSPEND_MAIN_FUNCTION import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl.Companion.DEFAULT_STATUS_FOR_SUSPEND_MAIN_FUNCTION
import org.jetbrains.kotlin.fir.declarations.impl.modifiersRepresentation import org.jetbrains.kotlin.fir.declarations.impl.modifiersRepresentation
import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType
import org.jetbrains.kotlin.fir.declarations.utils.nameOrSpecialName import org.jetbrains.kotlin.fir.declarations.utils.nameOrSpecialName
import org.jetbrains.kotlin.fir.expressions.FirBlock import org.jetbrains.kotlin.fir.expressions.FirBlock
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
@@ -32,10 +31,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.TypeAliasConstructorsSubstitutingSco
import org.jetbrains.kotlin.fir.scopes.impl.toConeType import org.jetbrains.kotlin.fir.scopes.impl.toConeType
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassifierSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.util.ListMultimap import org.jetbrains.kotlin.fir.util.ListMultimap
@@ -47,8 +43,8 @@ import org.jetbrains.kotlin.utils.SmartSet
val DEFAULT_STATUS_FOR_NORMAL_MAIN_FUNCTION = DEFAULT_STATUS_FOR_STATUSLESS_DECLARATIONS val DEFAULT_STATUS_FOR_NORMAL_MAIN_FUNCTION = DEFAULT_STATUS_FOR_STATUSLESS_DECLARATIONS
private val FirSimpleFunction.hasMainFunctionStatus private val FirNamedFunctionSymbol.hasMainFunctionStatus
get() = when (status.modifiersRepresentation) { get() = when (resolvedStatus.modifiersRepresentation) {
DEFAULT_STATUS_FOR_NORMAL_MAIN_FUNCTION.modifiersRepresentation, DEFAULT_STATUS_FOR_NORMAL_MAIN_FUNCTION.modifiersRepresentation,
DEFAULT_STATUS_FOR_SUSPEND_MAIN_FUNCTION.modifiersRepresentation, DEFAULT_STATUS_FOR_SUSPEND_MAIN_FUNCTION.modifiersRepresentation,
-> true -> true
@@ -57,76 +53,81 @@ private val FirSimpleFunction.hasMainFunctionStatus
private val CallableId.isTopLevel get() = className == null private val CallableId.isTopLevel get() = className == null
private fun FirDeclaration.isCollectable(): Boolean { private fun FirBasedSymbol<*>.isCollectable(): Boolean {
if (this is FirCallableDeclaration) { if (this is FirCallableSymbol<*>) {
if (contextReceivers.any { it.typeRef.coneType.hasError() }) return false if (resolvedContextReceivers.any { it.typeRef.coneType.hasError() }) return false
if (typeParameters.any { it.toConeType().hasError() }) return false if (typeParameterSymbols.any { it.toConeType().hasError() }) return false
if (receiverParameter?.typeRef?.coneType?.hasError() == true) return false if (receiverParameter?.typeRef?.coneType?.hasError() == true) return false
if (this is FirFunction && valueParameters.any { it.returnTypeRef.coneType.hasError() }) return false if (this is FirFunctionSymbol<*> && valueParameterSymbols.any { it.resolvedReturnType.hasError() }) return false
} }
return when (this) { return when (this) {
// - see tests with `fun () {}`. // - see tests with `fun () {}`.
// you can't redeclare something that has no name. // you can't redeclare something that has no name.
is FirSimpleFunction -> source?.kind !is KtFakeSourceElementKind && name != SpecialNames.NO_NAME_PROVIDED is FirNamedFunctionSymbol -> source?.kind !is KtFakeSourceElementKind && name != SpecialNames.NO_NAME_PROVIDED
is FirRegularClass -> name != SpecialNames.NO_NAME_PROVIDED is FirRegularClassSymbol -> name != SpecialNames.NO_NAME_PROVIDED
// - see testEnumValuesValueOf. // - see testEnumValuesValueOf.
// it generates a static function that has // it generates a static function that has
// the same signature as the function defined // the same signature as the function defined
// explicitly. // explicitly.
is FirProperty -> source?.kind !is KtFakeSourceElementKind.EnumGeneratedDeclaration is FirPropertySymbol -> source?.kind !is KtFakeSourceElementKind.EnumGeneratedDeclaration
// class delegation field will be renamed after by the IR backend in a case of a name clash // class delegation field will be renamed after by the IR backend in a case of a name clash
is FirField -> source?.kind != KtFakeSourceElementKind.ClassDelegationField is FirFieldSymbol -> source?.kind != KtFakeSourceElementKind.ClassDelegationField
else -> true else -> true
} }
} }
private fun isExpectAndActual(declaration1: FirDeclaration, declaration2: FirDeclaration): Boolean { private val FirBasedSymbol<*>.resolvedStatus
if (declaration1 !is FirMemberDeclaration) return false get() = when (this) {
if (declaration2 !is FirMemberDeclaration) return false is FirCallableSymbol<*> -> resolvedStatus
return (declaration1.status.isExpect && declaration2.status.isActual) || is FirClassLikeSymbol<*> -> resolvedStatus
(declaration1.status.isActual && declaration2.status.isExpect) else -> null
}
private fun isExpectAndActual(declaration1: FirBasedSymbol<*>, declaration2: FirBasedSymbol<*>): Boolean {
val status1 = declaration1.resolvedStatus ?: return false
val status2 = declaration2.resolvedStatus ?: return false
return (status1.isExpect && status2.isActual) || (status1.isActual && status2.isExpect)
} }
private class DeclarationBuckets { private class DeclarationBuckets {
val simpleFunctions = mutableListOf<Pair<FirSimpleFunction, String>>() val simpleFunctions = mutableListOf<Pair<FirNamedFunctionSymbol, String>>()
val constructors = mutableListOf<Pair<FirConstructor, String>>() val constructors = mutableListOf<Pair<FirConstructorSymbol, String>>()
val classLikes = mutableListOf<Pair<FirClassLikeDeclaration, String>>() val classLikes = mutableListOf<Pair<FirClassLikeSymbol<*>, String>>()
val properties = mutableListOf<Pair<FirProperty, String>>() val properties = mutableListOf<Pair<FirPropertySymbol, String>>()
val extensionProperties = mutableListOf<Pair<FirProperty, String>>() val extensionProperties = mutableListOf<Pair<FirPropertySymbol, String>>()
} }
private fun groupTopLevelByName(declarations: List<FirDeclaration>, context: CheckerContext): Map<Name, DeclarationBuckets> { private fun groupTopLevelByName(declarations: List<FirDeclaration>, context: CheckerContext): Map<Name, DeclarationBuckets> {
val groups = mutableMapOf<Name, DeclarationBuckets>() val groups = mutableMapOf<Name, DeclarationBuckets>()
for (declaration in declarations) { for (declaration in declarations) {
if (!declaration.isCollectable()) continue if (!declaration.symbol.isCollectable()) continue
when (declaration) { when (declaration) {
is FirSimpleFunction -> is FirSimpleFunction ->
groups.getOrPut(declaration.name, ::DeclarationBuckets).simpleFunctions += groups.getOrPut(declaration.name, ::DeclarationBuckets).simpleFunctions +=
declaration to FirRedeclarationPresenter.represent(declaration) declaration.symbol to FirRedeclarationPresenter.represent(declaration.symbol)
is FirProperty -> { is FirProperty -> {
val group = groups.getOrPut(declaration.name, ::DeclarationBuckets) val group = groups.getOrPut(declaration.name, ::DeclarationBuckets)
val representation = FirRedeclarationPresenter.represent(declaration) val representation = FirRedeclarationPresenter.represent(declaration.symbol)
if (declaration.receiverParameter != null) { if (declaration.receiverParameter != null) {
group.extensionProperties += declaration to representation group.extensionProperties += declaration.symbol to representation
} else { } else {
group.properties += declaration to representation group.properties += declaration.symbol to representation
} }
} }
is FirClassLikeDeclaration -> { is FirClassLikeDeclaration -> {
val representation = FirRedeclarationPresenter.represent(declaration) ?: continue val representation = FirRedeclarationPresenter.represent(declaration.symbol) ?: continue
val group = groups.getOrPut(declaration.nameOrSpecialName, ::DeclarationBuckets) val group = groups.getOrPut(declaration.nameOrSpecialName, ::DeclarationBuckets)
group.classLikes += declaration to representation group.classLikes += declaration.symbol to representation
declaration.expandedClassWithConstructorsScope(context)?.let { (expandedClass, scopeWithConstructors) -> declaration.symbol.expandedClassWithConstructorsScope(context)?.let { (expandedClass, scopeWithConstructors) ->
if (expandedClass.classKind == ClassKind.OBJECT) { if (expandedClass.classKind == ClassKind.OBJECT) {
return@let return@let
} }
@OptIn(SymbolInternals::class)
scopeWithConstructors.processDeclaredConstructors { scopeWithConstructors.processDeclaredConstructors {
group.constructors += it.fir to FirRedeclarationPresenter.represent(it.fir, declaration.symbol) group.constructors += it to FirRedeclarationPresenter.represent(it, declaration.symbol)
} }
} }
} }
@@ -137,9 +138,9 @@ private fun groupTopLevelByName(declarations: List<FirDeclaration>, context: Che
} }
/** /**
* Collects FirDeclarations for further analysis. * Collects symbols of FirDeclarations for further analysis.
*/ */
class FirDeclarationCollector<D : FirDeclaration>( class FirDeclarationCollector<D : FirBasedSymbol<*>>(
internal val context: CheckerContext, internal val context: CheckerContext,
) { ) {
internal val session: FirSession get() = context.sessionHolder.session internal val session: FirSession get() = context.sessionHolder.session
@@ -147,44 +148,44 @@ class FirDeclarationCollector<D : FirDeclaration>(
val declarationConflictingSymbols: HashMap<D, SmartSet<FirBasedSymbol<*>>> = hashMapOf() val declarationConflictingSymbols: HashMap<D, SmartSet<FirBasedSymbol<*>>> = hashMapOf()
} }
fun FirDeclarationCollector<FirDeclaration>.collectClassMembers(klass: FirRegularClass) { fun FirDeclarationCollector<FirBasedSymbol<*>>.collectClassMembers(klass: FirRegularClassSymbol) {
val otherDeclarations = mutableMapOf<String, MutableList<FirDeclaration>>() val otherDeclarations = mutableMapOf<String, MutableList<FirBasedSymbol<*>>>()
val functionDeclarations = mutableMapOf<String, MutableList<FirDeclaration>>() val functionDeclarations = mutableMapOf<String, MutableList<FirBasedSymbol<*>>>()
// TODO, KT-61243: Use declaredMemberScope // TODO, KT-61243: Use declaredMemberScope
for (it in klass.declarations) { @OptIn(SymbolInternals::class)
if (!it.isCollectable()) continue for (it in klass.fir.declarations) {
if (!it.symbol.isCollectable()) continue
when (it) { when (it) {
is FirSimpleFunction -> collect(it, FirRedeclarationPresenter.represent(it), functionDeclarations) is FirSimpleFunction -> collect(it.symbol, FirRedeclarationPresenter.represent(it.symbol), functionDeclarations)
is FirRegularClass -> collect(it, FirRedeclarationPresenter.represent(it), otherDeclarations) is FirRegularClass -> collect(it.symbol, FirRedeclarationPresenter.represent(it.symbol), otherDeclarations)
is FirTypeAlias -> collect(it, FirRedeclarationPresenter.represent(it), otherDeclarations) is FirTypeAlias -> collect(it.symbol, FirRedeclarationPresenter.represent(it.symbol), otherDeclarations)
is FirVariable -> collect(it, FirRedeclarationPresenter.represent(it), otherDeclarations) is FirVariable -> collect(it.symbol, FirRedeclarationPresenter.represent(it.symbol), otherDeclarations)
else -> {} else -> {}
} }
} }
} }
fun collectConflictingLocalFunctionsFrom(block: FirBlock, context: CheckerContext): Map<FirFunction, Set<FirBasedSymbol<*>>> { fun collectConflictingLocalFunctionsFrom(block: FirBlock, context: CheckerContext): Map<FirFunctionSymbol<*>, Set<FirBasedSymbol<*>>> {
val collectables = val collectables =
block.statements.filter { block.statements.filter {
(it is FirSimpleFunction || it is FirRegularClass) && (it as FirDeclaration).isCollectable() (it is FirSimpleFunction || it is FirRegularClass) && (it as FirDeclaration).symbol.isCollectable()
} }
if (collectables.isEmpty()) return emptyMap() if (collectables.isEmpty()) return emptyMap()
val inspector = FirDeclarationCollector<FirFunction>(context) val inspector = FirDeclarationCollector<FirFunctionSymbol<*>>(context)
val functionDeclarations = mutableMapOf<String, MutableList<FirFunction>>() val functionDeclarations = mutableMapOf<String, MutableList<FirFunctionSymbol<*>>>()
for (collectable in collectables) { for (collectable in collectables) {
when (collectable) { when (collectable) {
is FirSimpleFunction -> is FirSimpleFunction ->
inspector.collect(collectable, FirRedeclarationPresenter.represent(collectable), functionDeclarations) inspector.collect(collectable.symbol, FirRedeclarationPresenter.represent(collectable.symbol), functionDeclarations)
is FirClassLikeDeclaration -> { is FirClassLikeDeclaration -> {
collectable.expandedClassWithConstructorsScope(context)?.let { (_, scopeWithConstructors) -> collectable.symbol.expandedClassWithConstructorsScope(context)?.let { (_, scopeWithConstructors) ->
scopeWithConstructors.processDeclaredConstructors { scopeWithConstructors.processDeclaredConstructors {
@OptIn(SymbolInternals::class) inspector.collect(it, FirRedeclarationPresenter.represent(it, collectable.symbol), functionDeclarations)
inspector.collect(it.fir, FirRedeclarationPresenter.represent(it.fir, collectable.symbol), functionDeclarations)
} }
} }
} }
@@ -195,7 +196,7 @@ fun collectConflictingLocalFunctionsFrom(block: FirBlock, context: CheckerContex
return inspector.declarationConflictingSymbols return inspector.declarationConflictingSymbols
} }
private fun <D : FirDeclaration> FirDeclarationCollector<D>.collect( private fun <D : FirBasedSymbol<*>> FirDeclarationCollector<D>.collect(
declaration: D, declaration: D,
representation: String, representation: String,
map: MutableMap<String, MutableList<D>>, map: MutableMap<String, MutableList<D>>,
@@ -206,8 +207,8 @@ private fun <D : FirDeclaration> FirDeclarationCollector<D>.collect(
val conflicts = SmartSet.create<FirBasedSymbol<*>>() val conflicts = SmartSet.create<FirBasedSymbol<*>>()
for (otherDeclaration in it) { for (otherDeclaration in it) {
if (otherDeclaration != declaration && !isOverloadable(declaration, otherDeclaration, session)) { if (otherDeclaration != declaration && !isOverloadable(declaration, otherDeclaration, session)) {
conflicts.add(otherDeclaration.symbol) conflicts.add(otherDeclaration)
declarationConflictingSymbols.getOrPut(otherDeclaration) { SmartSet.create() }.add(declaration.symbol) declarationConflictingSymbols.getOrPut(otherDeclaration) { SmartSet.create() }.add(declaration)
} }
} }
@@ -233,16 +234,15 @@ private fun <D : FirDeclaration> FirDeclarationCollector<D>.collect(
* | constructors of classes | X | | | | | * | constructors of classes | X | | | | |
* | properties | | | X | X | X | * | properties | | | X | X | X |
*/ */
@OptIn(SymbolInternals::class)
@Suppress("GrazieInspection") @Suppress("GrazieInspection")
fun FirDeclarationCollector<FirDeclaration>.collectTopLevel(file: FirFile, packageMemberScope: FirPackageMemberScope) { fun FirDeclarationCollector<FirBasedSymbol<*>>.collectTopLevel(file: FirFile, packageMemberScope: FirPackageMemberScope) {
for ((declarationName, group) in groupTopLevelByName(file.declarations, context)) { for ((declarationName, group) in groupTopLevelByName(file.declarations, context)) {
val groupHasClassLikesOrProperties = group.classLikes.isNotEmpty() || group.properties.isNotEmpty() val groupHasClassLikesOrProperties = group.classLikes.isNotEmpty() || group.properties.isNotEmpty()
val groupHasSimpleFunctions = group.simpleFunctions.isNotEmpty() val groupHasSimpleFunctions = group.simpleFunctions.isNotEmpty()
fun collect( fun collect(
declarations: List<Pair<FirDeclaration, String>>, declarations: List<Pair<FirBasedSymbol<*>, String>>,
conflictingSymbol: FirBasedSymbol<*>, conflictingSymbol: FirBasedSymbol<*>,
conflictingPresentation: String? = null, conflictingPresentation: String? = null,
conflictingFile: FirFile? = null, conflictingFile: FirFile? = null,
@@ -270,19 +270,17 @@ fun FirDeclarationCollector<FirDeclaration>.collectTopLevel(file: FirFile, packa
collect(group.properties, conflictingSymbol, conflictingPresentation, conflictingFile) collect(group.properties, conflictingSymbol, conflictingPresentation, conflictingFile)
if (groupHasSimpleFunctions) { if (groupHasSimpleFunctions) {
val declaration = conflictingSymbol.fir if (conflictingSymbol !is FirClassLikeSymbol<*>) {
if (declaration !is FirClassLikeDeclaration) {
return return
} }
declaration.expandedClassWithConstructorsScope(context)?.let { (expandedClass, scopeWithConstructors) -> conflictingSymbol.expandedClassWithConstructorsScope(context)?.let { (expandedClass, scopeWithConstructors) ->
if (expandedClass.classKind == ClassKind.OBJECT || expandedClass.classKind == ClassKind.ENUM_ENTRY) { if (expandedClass.classKind == ClassKind.OBJECT || expandedClass.classKind == ClassKind.ENUM_ENTRY) {
return return
} }
scopeWithConstructors.processDeclaredConstructors { constructor -> scopeWithConstructors.processDeclaredConstructors { constructor ->
val ctorRepresentation = FirRedeclarationPresenter.represent(constructor.fir, declaration.symbol) val ctorRepresentation = FirRedeclarationPresenter.represent(constructor, conflictingSymbol)
collect(group.simpleFunctions, conflictingSymbol = constructor, conflictingPresentation = ctorRepresentation) collect(group.simpleFunctions, conflictingSymbol = constructor, conflictingPresentation = ctorRepresentation)
} }
} }
@@ -315,7 +313,7 @@ fun FirDeclarationCollector<FirDeclaration>.collectTopLevel(file: FirFile, packa
// session.nameConflictsTracker doesn't seem to work for LL API for redeclarations in the same file, for this reason // session.nameConflictsTracker doesn't seem to work for LL API for redeclarations in the same file, for this reason
// we explicitly check classLikes in the same file, too. // we explicitly check classLikes in the same file, too.
for ((classLike, representation) in group.classLikes) { for ((classLike, representation) in group.classLikes) {
collectFromClassifierSource(classLike.symbol, conflictingPresentation = representation, conflictingFile = file) collectFromClassifierSource(classLike, conflictingPresentation = representation, conflictingFile = file)
} }
} }
@@ -330,11 +328,11 @@ fun FirDeclarationCollector<FirDeclaration>.collectTopLevel(file: FirFile, packa
} }
} }
private fun FirClassLikeDeclaration.expandedClassWithConstructorsScope(context: CheckerContext): Pair<FirRegularClassSymbol, FirScope>? { private fun FirClassLikeSymbol<*>.expandedClassWithConstructorsScope(context: CheckerContext): Pair<FirRegularClassSymbol, FirScope>? {
return when (this) { return when (this) {
is FirRegularClass -> symbol to unsubstitutedScope(context) is FirRegularClassSymbol -> this to unsubstitutedScope(context)
is FirTypeAlias -> { is FirTypeAliasSymbol -> {
val expandedType = expandedConeType val expandedType = resolvedExpandedTypeRef.coneType as? ConeClassLikeType
val expandedClass = expandedType?.toRegularClassSymbol(context.session) val expandedClass = expandedType?.toRegularClassSymbol(context.session)
val expandedTypeScope = expandedType?.scope( val expandedTypeScope = expandedType?.scope(
context.session, context.scopeSession, context.session, context.scopeSession,
@@ -344,7 +342,7 @@ private fun FirClassLikeDeclaration.expandedClassWithConstructorsScope(context:
if (expandedType != null && expandedClass != null && expandedTypeScope != null) { if (expandedType != null && expandedClass != null && expandedTypeScope != null) {
val outerType = outerType(expandedType, context.session) { it.outerClassSymbol(context) } val outerType = outerType(expandedType, context.session) { it.outerClassSymbol(context) }
expandedClass to TypeAliasConstructorsSubstitutingScope(symbol, expandedTypeScope, outerType) expandedClass to TypeAliasConstructorsSubstitutingScope(this, expandedTypeScope, outerType)
} else { } else {
null null
} }
@@ -353,8 +351,8 @@ private fun FirClassLikeDeclaration.expandedClassWithConstructorsScope(context:
} }
} }
private fun FirDeclarationCollector<FirDeclaration>.collectTopLevelConflict( private fun FirDeclarationCollector<FirBasedSymbol<*>>.collectTopLevelConflict(
declaration: FirDeclaration, declaration: FirBasedSymbol<*>,
declarationPresentation: String, declarationPresentation: String,
containingFile: FirFile, containingFile: FirFile,
conflictingSymbol: FirBasedSymbol<*>, conflictingSymbol: FirBasedSymbol<*>,
@@ -362,10 +360,8 @@ private fun FirDeclarationCollector<FirDeclaration>.collectTopLevelConflict(
conflictingFile: FirFile? = null, conflictingFile: FirFile? = null,
) { ) {
conflictingSymbol.lazyResolveToPhase(FirResolvePhase.STATUS) conflictingSymbol.lazyResolveToPhase(FirResolvePhase.STATUS)
@OptIn(SymbolInternals::class) if (conflictingSymbol == declaration || declaration.moduleData != conflictingSymbol.moduleData) return
val conflicting = conflictingSymbol.fir val actualConflictingPresentation = conflictingPresentation ?: FirRedeclarationPresenter.represent(conflictingSymbol)
if (conflicting == declaration || declaration.moduleData != conflicting.moduleData) return
val actualConflictingPresentation = conflictingPresentation ?: FirRedeclarationPresenter.represent(conflicting)
if (actualConflictingPresentation != declarationPresentation) return if (actualConflictingPresentation != declarationPresentation) return
val actualConflictingFile = val actualConflictingFile =
conflictingFile ?: when (conflictingSymbol) { conflictingFile ?: when (conflictingSymbol) {
@@ -373,22 +369,24 @@ private fun FirDeclarationCollector<FirDeclaration>.collectTopLevelConflict(
is FirCallableSymbol<*> -> session.firProvider.getFirCallableContainerFile(conflictingSymbol) is FirCallableSymbol<*> -> session.firProvider.getFirCallableContainerFile(conflictingSymbol)
else -> null else -> null
} }
if (!conflicting.isCollectable()) return if (!conflictingSymbol.isCollectable()) return
if (areCompatibleMainFunctions(declaration, containingFile, conflicting, actualConflictingFile, session)) return if (areCompatibleMainFunctions(declaration, containingFile, conflictingSymbol, actualConflictingFile, session)) return
@OptIn(SymbolInternals::class)
val conflicting = conflictingSymbol.fir
if ( if (
conflicting is FirMemberDeclaration && conflicting is FirMemberDeclaration &&
!session.visibilityChecker.isVisible(conflicting, session, containingFile, emptyList(), dispatchReceiver = null) !session.visibilityChecker.isVisible(conflicting, session, containingFile, emptyList(), dispatchReceiver = null)
) return ) return
if (isOverloadable(declaration, conflicting, session)) return if (isOverloadable(declaration, conflictingSymbol, session)) return
declarationConflictingSymbols.getOrPut(declaration) { SmartSet.create() }.add(conflictingSymbol) declarationConflictingSymbols.getOrPut(declaration) { SmartSet.create() }.add(conflictingSymbol)
} }
private fun FirSimpleFunction.representsMainFunctionAllowingConflictingOverloads(session: FirSession): Boolean { private fun FirNamedFunctionSymbol.representsMainFunctionAllowingConflictingOverloads(session: FirSession): Boolean {
if (name != StandardNames.MAIN || !symbol.callableId.isTopLevel || !hasMainFunctionStatus) return false if (name != StandardNames.MAIN || !callableId.isTopLevel || !hasMainFunctionStatus) return false
if (receiverParameter != null || typeParameters.isNotEmpty()) return false if (receiverParameter != null || typeParameterSymbols.isNotEmpty()) return false
if (valueParameters.isEmpty()) return true if (valueParameterSymbols.isEmpty()) return true
val paramType = valueParameters.singleOrNull()?.returnTypeRef?.coneType?.fullyExpandedType(session) ?: return false val paramType = valueParameterSymbols.singleOrNull()?.resolvedReturnTypeRef?.coneType?.fullyExpandedType(session) ?: return false
if (!paramType.isNonPrimitiveArray) return false if (!paramType.isNonPrimitiveArray) return false
val typeArgument = paramType.typeArguments.singleOrNull() as? ConeKotlinTypeProjection ?: return false val typeArgument = paramType.typeArguments.singleOrNull() as? ConeKotlinTypeProjection ?: return false
// only Array<String> and Array<out String> are accepted // only Array<String> and Array<out String> are accepted
@@ -397,18 +395,18 @@ private fun FirSimpleFunction.representsMainFunctionAllowingConflictingOverloads
} }
private fun areCompatibleMainFunctions( private fun areCompatibleMainFunctions(
declaration1: FirDeclaration, file1: FirFile, declaration1: FirBasedSymbol<*>, file1: FirFile,
declaration2: FirDeclaration, file2: FirFile?, declaration2: FirBasedSymbol<*>, file2: FirFile?,
session: FirSession, session: FirSession,
) = file1 != file2 ) = file1 != file2
&& declaration1 is FirSimpleFunction && declaration1 is FirNamedFunctionSymbol
&& declaration2 is FirSimpleFunction && declaration2 is FirNamedFunctionSymbol
&& declaration1.representsMainFunctionAllowingConflictingOverloads(session) && declaration1.representsMainFunctionAllowingConflictingOverloads(session)
&& declaration2.representsMainFunctionAllowingConflictingOverloads(session) && declaration2.representsMainFunctionAllowingConflictingOverloads(session)
private fun isOverloadable( private fun isOverloadable(
declaration: FirDeclaration, declaration: FirBasedSymbol<*>,
conflicting: FirDeclaration, conflicting: FirBasedSymbol<*>,
session: FirSession, session: FirSession,
): Boolean { ): Boolean {
if (isExpectAndActual(declaration, conflicting)) return true if (isExpectAndActual(declaration, conflicting)) return true
@@ -417,8 +415,8 @@ private fun isOverloadable(
val conflictingIsLowPriority = hasLowPriorityAnnotation(conflicting.annotations) val conflictingIsLowPriority = hasLowPriorityAnnotation(conflicting.annotations)
if (declarationIsLowPriority != conflictingIsLowPriority) return true if (declarationIsLowPriority != conflictingIsLowPriority) return true
return declaration is FirCallableDeclaration && return declaration is FirCallableSymbol<*> &&
conflicting is FirCallableDeclaration && conflicting is FirCallableSymbol<*> &&
session.declarationOverloadabilityHelper.isOverloadable(declaration, conflicting) session.declarationOverloadabilityHelper.isOverloadable(declaration, conflicting)
} }
@@ -5,9 +5,9 @@
package org.jetbrains.kotlin.fir.analysis.checkers package org.jetbrains.kotlin.fir.analysis.checkers
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isOperator import org.jetbrains.kotlin.fir.declarations.utils.isOperator
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
@@ -34,18 +34,18 @@ internal object FirRedeclarationPresenter {
append(it.callableName) append(it.callableName)
} }
private fun StringBuilder.appendRepresentation(it: FirValueParameter) { private fun StringBuilder.appendRepresentation(it: FirValueParameterSymbol) {
if (it.isVararg) { if (it.isVararg) {
append("vararg ") append("vararg ")
} }
} }
private fun StringBuilder.appendRepresentationBeforeCallableId(it: FirCallableDeclaration) { private fun StringBuilder.appendRepresentationBeforeCallableId(it: FirCallableSymbol<*>) {
repeat(it.contextReceivers.size) { repeat(it.resolvedContextReceivers.size) {
append(',') append(',')
} }
append('<') append('<')
repeat(it.typeParameters.size) { repeat(it.typeParameterSymbols.size) {
append(',') append(',')
} }
append('>') append('>')
@@ -56,55 +56,55 @@ internal object FirRedeclarationPresenter {
append(']') append(']')
} }
private fun StringBuilder.appendValueParameters(it: FirSimpleFunction) { private fun StringBuilder.appendValueParameters(it: FirNamedFunctionSymbol) {
append('(') append('(')
it.valueParameters.forEach { it.valueParameterSymbols.forEach {
appendRepresentation(it) appendRepresentation(it)
append(',') append(',')
} }
append(')') append(')')
} }
fun represent(declaration: FirDeclaration): String? = when (declaration) { fun represent(declaration: FirBasedSymbol<*>): String? = when (declaration) {
is FirSimpleFunction -> represent(declaration) is FirNamedFunctionSymbol -> represent(declaration)
is FirRegularClass -> represent(declaration) is FirRegularClassSymbol -> represent(declaration)
is FirTypeAlias -> represent(declaration) is FirTypeAliasSymbol -> represent(declaration)
is FirProperty -> represent(declaration) is FirPropertySymbol -> represent(declaration)
else -> null else -> null
} }
fun represent(it: FirSimpleFunction) = buildString { fun represent(it: FirNamedFunctionSymbol) = buildString {
appendRepresentationBeforeCallableId(it) appendRepresentationBeforeCallableId(it)
if (it.isOperator) { if (it.isOperator) {
append("operator ") append("operator ")
} }
appendRepresentation(it.symbol.callableId) appendRepresentation(it.callableId)
appendValueParameters(it) appendValueParameters(it)
} }
fun represent(it: FirVariable) = buildString { fun represent(it: FirVariableSymbol<*>) = buildString {
appendRepresentationBeforeCallableId(it) appendRepresentationBeforeCallableId(it)
appendRepresentation(it.symbol.callableId) appendRepresentation(it.callableId)
} }
fun represent(it: FirTypeAlias) = representClassLike(it) fun represent(it: FirTypeAliasSymbol) = representClassLike(it)
fun represent(it: FirRegularClass) = representClassLike(it) fun represent(it: FirRegularClassSymbol) = representClassLike(it)
private fun representClassLike(it: FirClassLikeDeclaration) = buildString { private fun representClassLike(it: FirClassLikeSymbol<*>) = buildString {
append('<') append('<')
append('>') append('>')
append('[') append('[')
append(']') append(']')
appendRepresentation(it.symbol.classId) appendRepresentation(it.classId)
} }
fun represent(it: FirConstructor, owner: FirClassLikeSymbol<*>) = buildString { fun represent(it: FirConstructorSymbol, owner: FirClassLikeSymbol<*>) = buildString {
repeat(it.contextReceivers.size) { repeat(it.resolvedContextReceivers.size) {
append(',') append(',')
} }
append('<') append('<')
repeat(it.typeParameters.size) { repeat(it.typeParameterSymbols.size) {
append(',') append(',')
} }
append('>') append('>')
@@ -112,7 +112,7 @@ internal object FirRedeclarationPresenter {
append(']') append(']')
appendRepresentation(owner.classId) appendRepresentation(owner.classId)
append('(') append('(')
it.valueParameters.forEach { it.valueParameterSymbols.forEach {
appendRepresentation(it) appendRepresentation(it)
append(',') append(',')
} }
@@ -9,20 +9,18 @@ import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.FirNameConflictsTrackerComponent import org.jetbrains.kotlin.fir.FirNameConflictsTrackerComponent
import org.jetbrains.kotlin.fir.analysis.checkers.FirDeclarationCollector import org.jetbrains.kotlin.fir.analysis.checkers.*
import org.jetbrains.kotlin.fir.analysis.checkers.checkForLocalRedeclarations
import org.jetbrains.kotlin.fir.analysis.checkers.collectClassMembers
import org.jetbrains.kotlin.fir.analysis.checkers.collectTopLevel
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.packageFqName import org.jetbrains.kotlin.fir.packageFqName
import org.jetbrains.kotlin.fir.resolve.getContainingDeclaration
import org.jetbrains.kotlin.fir.scopes.impl.FirPackageMemberScope import org.jetbrains.kotlin.fir.scopes.impl.FirPackageMemberScope
import org.jetbrains.kotlin.fir.scopes.impl.PACKAGE_MEMBER import org.jetbrains.kotlin.fir.scopes.impl.PACKAGE_MEMBER
import org.jetbrains.kotlin.fir.scopes.impl.typeAliasForConstructor import org.jetbrains.kotlin.fir.scopes.impl.typeAliasForConstructor
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.utils.SmartSet import org.jetbrains.kotlin.utils.SmartSet
@@ -30,7 +28,7 @@ object FirConflictsDeclarationChecker : FirBasicDeclarationChecker() {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
when (declaration) { when (declaration) {
is FirFile -> { is FirFile -> {
val inspector = FirDeclarationCollector<FirDeclaration>(context) val inspector = FirDeclarationCollector<FirBasedSymbol<*>>(context)
checkFile(declaration, inspector, context) checkFile(declaration, inspector, context)
reportConflicts(reporter, context, inspector.declarationConflictingSymbols) reportConflicts(reporter, context, inspector.declarationConflictingSymbols)
} }
@@ -38,8 +36,8 @@ object FirConflictsDeclarationChecker : FirBasicDeclarationChecker() {
if (declaration.source?.kind !is KtFakeSourceElementKind) { if (declaration.source?.kind !is KtFakeSourceElementKind) {
checkForLocalRedeclarations(declaration.typeParameters, context, reporter) checkForLocalRedeclarations(declaration.typeParameters, context, reporter)
} }
val inspector = FirDeclarationCollector<FirDeclaration>(context) val inspector = FirDeclarationCollector<FirBasedSymbol<*>>(context)
inspector.collectClassMembers(declaration) inspector.collectClassMembers(declaration.symbol)
reportConflicts(reporter, context, inspector.declarationConflictingSymbols) reportConflicts(reporter, context, inspector.declarationConflictingSymbols)
} }
else -> { else -> {
@@ -56,18 +54,18 @@ object FirConflictsDeclarationChecker : FirBasicDeclarationChecker() {
private fun reportConflicts( private fun reportConflicts(
reporter: DiagnosticReporter, reporter: DiagnosticReporter,
context: CheckerContext, context: CheckerContext,
declarationConflictingSymbols: Map<FirDeclaration, SmartSet<FirBasedSymbol<*>>>, declarationConflictingSymbols: Map<FirBasedSymbol<*>, SmartSet<FirBasedSymbol<*>>>,
) { ) {
declarationConflictingSymbols.forEach { (conflictingDeclaration, symbols) -> declarationConflictingSymbols.forEach { (conflictingDeclaration, symbols) ->
val typeAliasForConstructorSource = (conflictingDeclaration as? FirConstructor)?.typeAliasForConstructor?.source val typeAliasForConstructorSource = (conflictingDeclaration as? FirConstructorSymbol)?.typeAliasForConstructor?.source
val source = typeAliasForConstructorSource ?: conflictingDeclaration.source val source = typeAliasForConstructorSource ?: conflictingDeclaration.source
if (symbols.isEmpty()) return@forEach if (symbols.isEmpty()) return@forEach
val factory = val factory =
if (conflictingDeclaration is FirSimpleFunction || conflictingDeclaration is FirConstructor) { if (conflictingDeclaration is FirNamedFunctionSymbol || conflictingDeclaration is FirConstructorSymbol) {
FirErrors.CONFLICTING_OVERLOADS FirErrors.CONFLICTING_OVERLOADS
} else if (conflictingDeclaration is FirClassLikeDeclaration && } else if (conflictingDeclaration is FirClassLikeSymbol<*> &&
conflictingDeclaration.getContainingDeclaration(context.session) == null && conflictingDeclaration.getContainingClassSymbol(context.session) == null &&
symbols.any { it is FirClassLikeSymbol<*> } symbols.any { it is FirClassLikeSymbol<*> }
) { ) {
FirErrors.PACKAGE_OR_CLASSIFIER_REDECLARATION FirErrors.PACKAGE_OR_CLASSIFIER_REDECLARATION
@@ -79,7 +77,7 @@ object FirConflictsDeclarationChecker : FirBasicDeclarationChecker() {
} }
} }
private fun checkFile(file: FirFile, inspector: FirDeclarationCollector<FirDeclaration>, context: CheckerContext) { private fun checkFile(file: FirFile, inspector: FirDeclarationCollector<FirBasedSymbol<*>>, context: CheckerContext) {
val packageMemberScope: FirPackageMemberScope = context.sessionHolder.scopeSession.getOrBuild(file.packageFqName, PACKAGE_MEMBER) { val packageMemberScope: FirPackageMemberScope = context.sessionHolder.scopeSession.getOrBuild(file.packageFqName, PACKAGE_MEMBER) {
FirPackageMemberScope(file.packageFqName, context.sessionHolder.session) FirPackageMemberScope(file.packageFqName, context.sessionHolder.session)
} }
@@ -7,9 +7,10 @@ package org.jetbrains.kotlin.fir.declarations
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent import org.jetbrains.kotlin.fir.FirSessionComponent
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
interface FirDeclarationOverloadabilityHelper : FirSessionComponent { interface FirDeclarationOverloadabilityHelper : FirSessionComponent {
fun isOverloadable(a: FirCallableDeclaration, b: FirCallableDeclaration): Boolean fun isOverloadable(a: FirCallableSymbol<*>, b: FirCallableSymbol<*>): Boolean
} }
val FirSession.declarationOverloadabilityHelper: FirDeclarationOverloadabilityHelper by FirSession.sessionComponentAccessor() val FirSession.declarationOverloadabilityHelper: FirDeclarationOverloadabilityHelper by FirSession.sessionComponentAccessor()
@@ -6,19 +6,17 @@
package org.jetbrains.kotlin.fir.resolve.calls package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOverloadabilityHelper
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.typeSpecificityComparatorProvider
import org.jetbrains.kotlin.fir.declarations.utils.isExpect import org.jetbrains.kotlin.fir.declarations.utils.isExpect
import org.jetbrains.kotlin.fir.declarations.utils.isSynthetic
import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.resolve.calls.results.* import org.jetbrains.kotlin.resolve.calls.results.*
import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.KotlinTypeMarker
class FirDeclarationOverloadabilityHelperImpl(val session: FirSession) : FirDeclarationOverloadabilityHelper { class FirDeclarationOverloadabilityHelperImpl(val session: FirSession) : FirDeclarationOverloadabilityHelper {
override fun isOverloadable(a: FirCallableDeclaration, b: FirCallableDeclaration): Boolean { override fun isOverloadable(a: FirCallableSymbol<*>, b: FirCallableSymbol<*>): Boolean {
val sigA = createSignature(a) val sigA = createSignature(a)
val sigB = createSignature(b) val sigB = createSignature(b)
@@ -26,8 +24,8 @@ class FirDeclarationOverloadabilityHelperImpl(val session: FirSession) : FirDecl
} }
private fun isNotLessSpecific( private fun isNotLessSpecific(
sigA: FlatSignature<FirCallableDeclaration>, sigA: FlatSignature<FirCallableSymbol<*>>,
sigB: FlatSignature<FirCallableDeclaration>, sigB: FlatSignature<FirCallableSymbol<*>>,
): Boolean = createEmptyConstraintSystem().isSignatureNotLessSpecific( ): Boolean = createEmptyConstraintSystem().isSignatureNotLessSpecific(
sigA, sigA,
sigB, sigB,
@@ -35,23 +33,23 @@ class FirDeclarationOverloadabilityHelperImpl(val session: FirSession) : FirDecl
session.typeSpecificityComparatorProvider?.typeSpecificityComparator ?: TypeSpecificityComparator.NONE, session.typeSpecificityComparatorProvider?.typeSpecificityComparator ?: TypeSpecificityComparator.NONE,
) )
private fun createSignature(declaration: FirCallableDeclaration): FlatSignature<FirCallableDeclaration> { private fun createSignature(declaration: FirCallableSymbol<*>): FlatSignature<FirCallableSymbol<*>> {
val valueParameters = (declaration as? FirFunction)?.valueParameters.orEmpty() val valueParameters = (declaration as? FirFunctionSymbol<*>)?.valueParameterSymbols.orEmpty()
return FlatSignature( return FlatSignature(
origin = declaration, origin = declaration,
typeParameters = declaration.typeParameters.map { it.symbol.toLookupTag() }, typeParameters = declaration.typeParameterSymbols.map { it.toLookupTag() },
valueParameterTypes = buildList<KotlinTypeMarker> { valueParameterTypes = buildList<KotlinTypeMarker> {
declaration.contextReceivers.mapTo(this) { it.typeRef.coneType } declaration.resolvedContextReceivers.mapTo(this) { it.typeRef.coneType }
declaration.receiverParameter?.let { add(it.typeRef.coneType) } declaration.receiverParameter?.let { add(it.typeRef.coneType) }
valueParameters.mapTo(this) { it.returnTypeRef.coneType } valueParameters.mapTo(this) { it.resolvedReturnType }
}, },
hasExtensionReceiver = declaration.receiverParameter != null, hasExtensionReceiver = declaration.receiverParameter != null,
contextReceiverCount = declaration.contextReceivers.size, contextReceiverCount = declaration.resolvedContextReceivers.size,
hasVarargs = valueParameters.any { it.isVararg }, hasVarargs = valueParameters.any { it.isVararg },
numDefaults = 0, numDefaults = 0,
isExpect = declaration.isExpect, isExpect = declaration.isExpect,
isSyntheticMember = declaration.isSynthetic isSyntheticMember = declaration.origin is FirDeclarationOrigin.Synthetic
) )
} }