diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConflictsHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConflictsHelpers.kt index 5b510cf05d5..24674e207de 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConflictsHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConflictsHelpers.kt @@ -6,20 +6,30 @@ package org.jetbrains.kotlin.fir.analysis.checkers import org.jetbrains.kotlin.KtFakeSourceElementKind +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.reportOn -import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirNameConflictsTracker import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.impl.FirOuterClassTypeParameterRef import org.jetbrains.kotlin.fir.declarations.utils.isOperator +import org.jetbrains.kotlin.fir.resolve.providers.firProvider +import org.jetbrains.kotlin.fir.scopes.impl.FirPackageMemberScope import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.SymbolInternals +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.util.ListMultimap +import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.utils.SmartSet /** * Provides representations for FirElement's. @@ -248,11 +258,11 @@ private fun FirDeclaration.isCollectable() = when (this) { /** * Collects FirDeclarations for further analysis. */ -open class FirDeclarationInspector( - protected val presenter: FirDeclarationPresenter = FirDefaultDeclarationPresenter() -) { - val otherDeclarations = mutableMapOf>() - val functionDeclarations = mutableMapOf>() +class FirDeclarationInspector { + val declarationConflictingSymbols: HashMap>> = hashMapOf() + private val presenter: FirDeclarationPresenter = FirDefaultDeclarationPresenter() + private val otherDeclarations = mutableMapOf>() + private val functionDeclarations = mutableMapOf>() fun collect(declaration: FirDeclaration) { when { @@ -264,26 +274,222 @@ open class FirDeclarationInspector( } } - protected open fun collectNonFunctionDeclaration(key: String, declaration: FirDeclaration): MutableList = + fun collectWithExternalConflicts( + declaration: FirDeclaration, + containingFile: FirFile, + session: FirSession, + packageMemberScope: FirPackageMemberScope + ) { + collect(declaration) + var declarationName: Name? = null + val declarationPresentation = presenter.represent(declaration) ?: return + + when (declaration) { + is FirSimpleFunction -> { + declarationName = declaration.name + if (!declarationName.isSpecial) { + packageMemberScope.processFunctionsByName(declarationName) { + collectExternalConflict( + declaration, declarationPresentation, containingFile, it, null, null, session + ) + } + packageMemberScope.processClassifiersByNameWithSubstitution(declarationName) { symbol, _ -> + symbol.lazyResolveToPhase(FirResolvePhase.STATUS) + @OptIn(SymbolInternals::class) + val classWithSameName = symbol.fir as? FirRegularClass + classWithSameName?.onConstructors { constructor -> + collectExternalConflict( + declaration, declarationPresentation, containingFile, + constructor.symbol, presenter.represent(constructor, classWithSameName), null, + session + ) + } + } + } + } + is FirVariable -> { + declarationName = declaration.name + if (!declarationName.isSpecial) { + packageMemberScope.processPropertiesByName(declarationName) { + collectExternalConflict( + declaration, declarationPresentation, containingFile, it, null, null, session + ) + } + } + } + is FirRegularClass -> { + declarationName = declaration.name + + if (!declarationName.isSpecial) { + packageMemberScope.processClassifiersByNameWithSubstitution(declarationName) { symbol, _ -> + collectExternalConflict( + declaration, declarationPresentation, containingFile, symbol, null, null, session + ) + } + declaration.onConstructors { constructor -> + packageMemberScope.processFunctionsByName(declarationName!!) { + collectExternalConflict( + constructor, presenter.represent(constructor, declaration), containingFile, + it, null, null, session + ) + } + } + + session.nameConflictsTracker?.let { it as? FirNameConflictsTracker } + ?.redeclaredClassifiers?.get(declaration.symbol.classId)?.forEach { + collectExternalConflict( + declaration, + declarationPresentation, + containingFile, + it.classifier, + null, + it.file, + session + ) + } + } + } + is FirTypeAlias -> { + declarationName = declaration.name + if (!declarationName.isSpecial) { + packageMemberScope.processClassifiersByNameWithSubstitution(declarationName) { symbol, _ -> + collectExternalConflict( + declaration, declarationPresentation, containingFile, symbol, null, null, session + ) + } + session.nameConflictsTracker?.let { it as? FirNameConflictsTracker } + ?.redeclaredClassifiers?.get(declaration.symbol.classId)?.forEach { + collectExternalConflict( + declaration, + declarationPresentation, + containingFile, + it.classifier, + null, + it.file, + session + ) + } + } + } + else -> { + } + } + if (declarationName != null) { + session.lookupTracker?.recordLookup( + declarationName, containingFile.packageFqName.asString(), declaration.source, containingFile.source + ) + } + } + + private fun collectNonFunctionDeclaration(key: String, declaration: FirDeclaration): MutableList = otherDeclarations.getOrPut(key) { mutableListOf() }.also { it.add(declaration) + collectLocalConflicts(declaration, it) } - protected open fun collectFunction(key: String, declaration: FirSimpleFunction): MutableList = + private fun collectFunction(key: String, declaration: FirSimpleFunction): MutableList = functionDeclarations.getOrPut(key) { mutableListOf() }.also { it.add(declaration) + collectLocalConflicts(declaration, it) } - fun contains(declaration: FirDeclaration) = when (declaration) { - is FirSimpleFunction -> presenter.represent(declaration) in functionDeclarations - is FirRegularClass -> presenter.represent(declaration) in otherDeclarations - is FirTypeAlias -> presenter.represent(declaration) in otherDeclarations - is FirProperty -> presenter.represent(declaration) in otherDeclarations - else -> false + private fun collectLocalConflicts(declaration: FirDeclaration, conflicting: List) { + val localConflicts = SmartSet.create>() + for (otherDeclaration in conflicting) { + if (otherDeclaration is FirField && otherDeclaration.source?.kind == KtFakeSourceElementKind.ClassDelegationField) { + // class delegation field will be renamed after by the IR backend in a case of a name clash + continue + } + if (otherDeclaration != declaration && !isExpectAndActual(declaration, otherDeclaration)) { + localConflicts.add(otherDeclaration.symbol) + declarationConflictingSymbols.getOrPut(otherDeclaration) { SmartSet.create() }.add(declaration.symbol) + } + } + declarationConflictingSymbols[declaration] = localConflicts + } + + private fun isExpectAndActual(declaration1: FirDeclaration, declaration2: FirDeclaration): Boolean { + if (declaration1 !is FirMemberDeclaration) return false + if (declaration2 !is FirMemberDeclaration) return false + return (declaration1.status.isExpect && declaration2.status.isActual) || + (declaration1.status.isActual && declaration2.status.isExpect) + } + + private fun areCompatibleMainFunctions( + declaration1: FirDeclaration, file1: FirFile, declaration2: FirDeclaration, file2: FirFile? + ): Boolean { + // TODO: proper main function detector + if (declaration1 !is FirSimpleFunction || declaration2 !is FirSimpleFunction) return false + if (declaration1.name.asString() != "main" || declaration2.name.asString() != "main") return false + return file1 != file2 + } + + private fun collectExternalConflict( + declaration: FirDeclaration, + declarationPresentation: String, + containingFile: FirFile, + conflictingSymbol: FirBasedSymbol<*>, + conflictingPresentation: String?, + conflictingFile: FirFile?, + session: FirSession + ) { + conflictingSymbol.lazyResolveToPhase(FirResolvePhase.STATUS) + @OptIn(SymbolInternals::class) + val conflicting = conflictingSymbol.fir + if (conflicting == declaration || declaration.moduleData != conflicting.moduleData) return + val actualConflictingPresentation = conflictingPresentation ?: presenter.represent(conflicting) + if (actualConflictingPresentation != declarationPresentation) return + val actualConflictingFile = + conflictingFile ?: when (conflictingSymbol) { + is FirClassLikeSymbol<*> -> session.firProvider.getFirClassifierContainerFileIfAny(conflictingSymbol) + is FirCallableSymbol<*> -> session.firProvider.getFirCallableContainerFile(conflictingSymbol) + else -> null + } + if (containingFile == actualConflictingFile && conflicting.origin == FirDeclarationOrigin.Precompiled) { + return // TODO: rewrite local decls checker to the same logic and then remove the check + } + if (areCompatibleMainFunctions(declaration, containingFile, conflicting, actualConflictingFile)) return + if (isExpectAndActual(declaration, conflicting)) return + if ( + conflicting is FirMemberDeclaration && + !session.visibilityChecker.isVisible(conflicting, session, containingFile, emptyList(), dispatchReceiver = null) + ) return + val declarationIsLowPriority = hasLowPriorityAnnotation(declaration.annotations) + val conflictingIsLowPriority = hasLowPriorityAnnotation(conflicting.annotations) + if (declarationIsLowPriority != conflictingIsLowPriority) return + declarationConflictingSymbols.getOrPut(declaration) { SmartSet.create() }.add(conflictingSymbol) + } + + private fun FirDeclarationPresenter.represent(declaration: FirDeclaration): String? = + when (declaration) { + is FirSimpleFunction -> represent(declaration) + is FirRegularClass -> represent(declaration) + is FirTypeAlias -> represent(declaration) + is FirProperty -> represent(declaration) + else -> null + } + + private fun FirRegularClass.onConstructors(action: (ctor: FirConstructor) -> Unit) { + class ClassConstructorVisitor : FirVisitorVoid() { + override fun visitElement(element: FirElement) {} + + override fun visitConstructor(constructor: FirConstructor) { + action(constructor) + } + + override fun visitDeclarationStatus(declarationStatus: FirDeclarationStatus) {} + override fun visitRegularClass(regularClass: FirRegularClass) {} + override fun visitProperty(property: FirProperty) {} + override fun visitSimpleFunction(simpleFunction: FirSimpleFunction) {} + } + + if (classKind != ClassKind.OBJECT && classKind != ClassKind.ENUM_ENTRY) { + acceptChildren(ClassConstructorVisitor()) + } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt index ab7d47789b1..d0d9ba8f82a 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirConflictsDeclarationChecker.kt @@ -6,234 +6,35 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration import org.jetbrains.kotlin.KtFakeSourceElementKind -import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.diagnostics.reportOn -import org.jetbrains.kotlin.fir.* +import org.jetbrains.kotlin.fir.FirNameConflictsTrackerComponent import org.jetbrains.kotlin.fir.analysis.checkers.FirDeclarationInspector -import org.jetbrains.kotlin.fir.analysis.checkers.FirDeclarationPresenter import org.jetbrains.kotlin.fir.analysis.checkers.checkConflictingElements 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.packageFqName import org.jetbrains.kotlin.fir.resolve.getContainingDeclaration -import org.jetbrains.kotlin.fir.resolve.providers.firProvider import org.jetbrains.kotlin.fir.scopes.impl.FirPackageMemberScope import org.jetbrains.kotlin.fir.scopes.impl.PACKAGE_MEMBER -import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol -import org.jetbrains.kotlin.fir.symbols.SymbolInternals -import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol -import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase -import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.utils.SmartSet object FirConflictsDeclarationChecker : FirBasicDeclarationChecker() { - - private class DeclarationInspector : FirDeclarationInspector() { - - val declarationConflictingSymbols: HashMap>> = hashMapOf() - - override fun collectNonFunctionDeclaration(key: String, declaration: FirDeclaration): MutableList = - super.collectNonFunctionDeclaration(key, declaration).also { - collectLocalConflicts(declaration, it) - } - - override fun collectFunction(key: String, declaration: FirSimpleFunction): MutableList = - super.collectFunction(key, declaration).also { - collectLocalConflicts(declaration, it) - } - - private fun collectLocalConflicts(declaration: FirDeclaration, conflicting: List) { - val localConflicts = SmartSet.create>() - for (otherDeclaration in conflicting) { - if (otherDeclaration is FirField && otherDeclaration.source?.kind == KtFakeSourceElementKind.ClassDelegationField) { - // class delegation field will be renamed after by the IR backend in a case of a name clash - continue - } - if (otherDeclaration != declaration && !isExpectAndActual(declaration, otherDeclaration)) { - localConflicts.add(otherDeclaration.symbol) - declarationConflictingSymbols.getOrPut(otherDeclaration) { SmartSet.create() }.add(declaration.symbol) - } - } - declarationConflictingSymbols[declaration] = localConflicts - } - - private fun isExpectAndActual(declaration1: FirDeclaration, declaration2: FirDeclaration): Boolean { - if (declaration1 !is FirMemberDeclaration) return false - if (declaration2 !is FirMemberDeclaration) return false - return (declaration1.status.isExpect && declaration2.status.isActual) || - (declaration1.status.isActual && declaration2.status.isExpect) - } - - private fun areCompatibleMainFunctions( - declaration1: FirDeclaration, file1: FirFile, declaration2: FirDeclaration, file2: FirFile? - ): Boolean { - // TODO: proper main function detector - if (declaration1 !is FirSimpleFunction || declaration2 !is FirSimpleFunction) return false - if (declaration1.name.asString() != "main" || declaration2.name.asString() != "main") return false - return file1 != file2 - } - - private fun collectExternalConflict( - declaration: FirDeclaration, - declarationPresentation: String, - containingFile: FirFile, - conflictingSymbol: FirBasedSymbol<*>, - conflictingPresentation: String?, - conflictingFile: FirFile?, - session: FirSession - ) { - conflictingSymbol.lazyResolveToPhase(FirResolvePhase.STATUS) - @OptIn(SymbolInternals::class) - val conflicting = conflictingSymbol.fir - if (conflicting == declaration || declaration.moduleData != conflicting.moduleData) return - val actualConflictingPresentation = conflictingPresentation ?: presenter.represent(conflicting) - if (actualConflictingPresentation != declarationPresentation) return - val actualConflictingFile = - conflictingFile ?: when (conflictingSymbol) { - is FirClassLikeSymbol<*> -> session.firProvider.getFirClassifierContainerFileIfAny(conflictingSymbol) - is FirCallableSymbol<*> -> session.firProvider.getFirCallableContainerFile(conflictingSymbol) - else -> null - } - if (containingFile == actualConflictingFile && conflicting.origin == FirDeclarationOrigin.Precompiled) { - return // TODO: rewrite local decls checker to the same logic and then remove the check - } - if (areCompatibleMainFunctions(declaration, containingFile, conflicting, actualConflictingFile)) return - if (isExpectAndActual(declaration, conflicting)) return - if ( - conflicting is FirMemberDeclaration && - !session.visibilityChecker.isVisible(conflicting, session, containingFile, emptyList(), dispatchReceiver = null) - ) return - val declarationIsLowPriority = hasLowPriorityAnnotation(declaration.annotations) - val conflictingIsLowPriority = hasLowPriorityAnnotation(conflicting.annotations) - if (declarationIsLowPriority != conflictingIsLowPriority) return - declarationConflictingSymbols.getOrPut(declaration) { SmartSet.create() }.add(conflictingSymbol) - } - - fun collectWithExternalConflicts( - declaration: FirDeclaration, - containingFile: FirFile, - session: FirSession, - packageMemberScope: FirPackageMemberScope - ) { - collect(declaration) - var declarationName: Name? = null - val declarationPresentation = presenter.represent(declaration) ?: return - - when (declaration) { - is FirSimpleFunction -> { - declarationName = declaration.name - if (!declarationName.isSpecial) { - packageMemberScope.processFunctionsByName(declarationName) { - collectExternalConflict( - declaration, declarationPresentation, containingFile, it, null, null, session - ) - } - packageMemberScope.processClassifiersByNameWithSubstitution(declarationName) { symbol, _ -> - symbol.lazyResolveToPhase(FirResolvePhase.STATUS) - @OptIn(SymbolInternals::class) - val classWithSameName = symbol.fir as? FirRegularClass - classWithSameName?.onConstructors { constructor -> - collectExternalConflict( - declaration, declarationPresentation, containingFile, - constructor.symbol, presenter.represent(constructor, classWithSameName), null, - session - ) - } - } - } - } - is FirVariable -> { - declarationName = declaration.name - if (!declarationName.isSpecial) { - packageMemberScope.processPropertiesByName(declarationName) { - collectExternalConflict( - declaration, declarationPresentation, containingFile, it, null, null, session - ) - } - } - } - is FirRegularClass -> { - declarationName = declaration.name - - if (!declarationName.isSpecial) { - packageMemberScope.processClassifiersByNameWithSubstitution(declarationName) { symbol, _ -> - collectExternalConflict( - declaration, declarationPresentation, containingFile, symbol, null, null, session - ) - } - declaration.onConstructors { constructor -> - packageMemberScope.processFunctionsByName(declarationName!!) { - collectExternalConflict( - constructor, presenter.represent(constructor, declaration), containingFile, - it, null, null, session - ) - } - } - - session.nameConflictsTracker?.let { it as? FirNameConflictsTracker } - ?.redeclaredClassifiers?.get(declaration.symbol.classId)?.forEach { - collectExternalConflict( - declaration, - declarationPresentation, - containingFile, - it.classifier, - null, - it.file, - session - ) - } - } - } - is FirTypeAlias -> { - declarationName = declaration.name - if (!declarationName.isSpecial) { - packageMemberScope.processClassifiersByNameWithSubstitution(declarationName) { symbol, _ -> - collectExternalConflict( - declaration, declarationPresentation, containingFile, symbol, null, null, session - ) - } - session.nameConflictsTracker?.let { it as? FirNameConflictsTracker } - ?.redeclaredClassifiers?.get(declaration.symbol.classId)?.forEach { - collectExternalConflict( - declaration, - declarationPresentation, - containingFile, - it.classifier, - null, - it.file, - session - ) - } - } - } - else -> { - } - } - if (declarationName != null) { - session.lookupTracker?.recordLookup( - declarationName, containingFile.packageFqName.asString(), declaration.source, containingFile.source - ) - } - } - } - override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { - val inspector: DeclarationInspector? + val inspector: FirDeclarationInspector? when (declaration) { is FirFile -> { - inspector = DeclarationInspector() + inspector = FirDeclarationInspector() checkFile(declaration, inspector, context) } is FirRegularClass -> { if (declaration.source?.kind !is KtFakeSourceElementKind) { checkConflictingElements(declaration.typeParameters, context, reporter) } - inspector = DeclarationInspector() + inspector = FirDeclarationInspector() checkRegularClass(declaration, inspector) } else -> { @@ -271,7 +72,7 @@ object FirConflictsDeclarationChecker : FirBasicDeclarationChecker() { } } - private fun checkFile(file: FirFile, inspector: DeclarationInspector, context: CheckerContext) { + private fun checkFile(file: FirFile, inspector: FirDeclarationInspector, context: CheckerContext) { val packageMemberScope: FirPackageMemberScope = context.sessionHolder.scopeSession.getOrBuild(file.packageFqName, PACKAGE_MEMBER) { FirPackageMemberScope(file.packageFqName, context.sessionHolder.session) } @@ -280,22 +81,13 @@ object FirConflictsDeclarationChecker : FirBasicDeclarationChecker() { } } - private fun checkRegularClass(declaration: FirRegularClass, inspector: DeclarationInspector) { + private fun checkRegularClass(declaration: FirRegularClass, inspector: FirDeclarationInspector) { for (it in declaration.declarations) { inspector.collect(it) } } } -private fun FirDeclarationPresenter.represent(declaration: FirDeclaration): String? = - when (declaration) { - is FirSimpleFunction -> represent(declaration) - is FirRegularClass -> represent(declaration) - is FirTypeAlias -> represent(declaration) - is FirProperty -> represent(declaration) - else -> null - } - class FirNameConflictsTracker : FirNameConflictsTrackerComponent() { data class ClassifierWithFile( @@ -316,22 +108,4 @@ class FirNameConflictsTracker : FirNameConflictsTrackerComponent() { } } -private fun FirRegularClass.onConstructors(action: (ctor: FirConstructor) -> Unit) { - class ClassConstructorVisitor : FirVisitorVoid() { - override fun visitElement(element: FirElement) {} - - override fun visitConstructor(constructor: FirConstructor) { - action(constructor) - } - - override fun visitDeclarationStatus(declarationStatus: FirDeclarationStatus) {} - override fun visitRegularClass(regularClass: FirRegularClass) {} - override fun visitProperty(property: FirProperty) {} - override fun visitSimpleFunction(simpleFunction: FirSimpleFunction) {} - } - - if (classKind != ClassKind.OBJECT && classKind != ClassKind.ENUM_ENTRY) { - acceptChildren(ClassConstructorVisitor()) - } -}