[FIR] Check conflicting overloads via scopes
Scopes may return private symbols from supertypes, they should not clash with symbols from the current class. For example, see: `FirLightTreeBlackBoxCodegenWithIrFakeOverrideGeneratorTestGenerated.FakeOverride#testPrivateFakeOverrides1` Lombok shouldn't generate functions if the user has defined explicit ones. In K1 generated functions are not really added to the declared members scope. ^KT-61243 Fixed
This commit is contained in:
committed by
Space Team
parent
973248f432
commit
4e58715760
+61
-22
@@ -24,8 +24,7 @@ import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.outerType
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.scope
|
||||
import org.jetbrains.kotlin.fir.scopes.CallableCopyTypeCalculator
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||
import org.jetbrains.kotlin.fir.scopes.*
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirPackageMemberScope
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.TypeAliasConstructorsSubstitutingScope
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.toConeType
|
||||
@@ -151,32 +150,72 @@ class FirDeclarationCollector<D : FirBasedSymbol<*>>(
|
||||
fun FirDeclarationCollector<FirBasedSymbol<*>>.collectClassMembers(klass: FirRegularClassSymbol) {
|
||||
val otherDeclarations = mutableMapOf<String, MutableList<FirBasedSymbol<*>>>()
|
||||
val functionDeclarations = mutableMapOf<String, MutableList<FirBasedSymbol<*>>>()
|
||||
val scope = klass.unsubstitutedScope(context)
|
||||
|
||||
// TODO, KT-61243: Use declaredMemberScope
|
||||
@OptIn(SymbolInternals::class)
|
||||
for (it in klass.fir.declarations) {
|
||||
if (!it.symbol.isCollectable()) continue
|
||||
scope.collectLeafFunctions().forEach {
|
||||
if (it.isCollectable() && it.isVisibleInClass(klass)) {
|
||||
collect(it, FirRedeclarationPresenter.represent(it), functionDeclarations)
|
||||
}
|
||||
}
|
||||
|
||||
when (it) {
|
||||
is FirSimpleFunction -> collect(it.symbol, FirRedeclarationPresenter.represent(it.symbol), functionDeclarations)
|
||||
is FirRegularClass -> {
|
||||
collect(it.symbol, FirRedeclarationPresenter.represent(it.symbol), otherDeclarations)
|
||||
val visitedProperties = mutableSetOf<FirVariableSymbol<*>>()
|
||||
|
||||
// Objects have implicit FirPrimaryConstructors
|
||||
if (it.symbol.classKind == ClassKind.OBJECT) {
|
||||
continue
|
||||
}
|
||||
scope.collectLeafProperties().forEach {
|
||||
if (it.isCollectable() && it.isVisibleInClass(klass)) {
|
||||
collect(it, FirRedeclarationPresenter.represent(it), otherDeclarations)
|
||||
}
|
||||
visitedProperties.add(it)
|
||||
}
|
||||
|
||||
it.symbol.expandedClassWithConstructorsScope(context)?.let { (_, scopeWithConstructors) ->
|
||||
scopeWithConstructors.processDeclaredConstructors { constructor ->
|
||||
collect(constructor, FirRedeclarationPresenter.represent(constructor, it.symbol), functionDeclarations)
|
||||
}
|
||||
}
|
||||
}
|
||||
is FirTypeAlias -> collect(it.symbol, FirRedeclarationPresenter.represent(it.symbol), otherDeclarations)
|
||||
is FirVariable -> collect(it.symbol, FirRedeclarationPresenter.represent(it.symbol), otherDeclarations)
|
||||
klass.declaredMemberScope(context).processAllProperties {
|
||||
if (it !in visitedProperties && it.isCollectable()) {
|
||||
collect(it, FirRedeclarationPresenter.represent(it), otherDeclarations)
|
||||
}
|
||||
}
|
||||
|
||||
fun processClassifier(it: FirClassifierSymbol<*>) {
|
||||
when {
|
||||
!it.isCollectable() || !it.isVisibleInClass(klass) -> return
|
||||
it is FirRegularClassSymbol -> collect(it, FirRedeclarationPresenter.represent(it), otherDeclarations)
|
||||
it is FirTypeAliasSymbol -> collect(it, FirRedeclarationPresenter.represent(it), otherDeclarations)
|
||||
else -> {}
|
||||
}
|
||||
|
||||
// This `if` can't be merged with the `when`
|
||||
// above, because otherwise the smartcast
|
||||
// below doesn't work.
|
||||
if (it !is FirClassLikeSymbol<*>) {
|
||||
return
|
||||
}
|
||||
|
||||
it.expandedClassWithConstructorsScope(context)?.let { (expandedClass, scopeWithConstructors) ->
|
||||
// Objects have implicit FirPrimaryConstructors
|
||||
if (expandedClass.classKind == ClassKind.OBJECT) {
|
||||
return@let
|
||||
}
|
||||
|
||||
scopeWithConstructors.processDeclaredConstructors { constructor ->
|
||||
collect(constructor, FirRedeclarationPresenter.represent(constructor, it), functionDeclarations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val visitedClassifiers = mutableSetOf<FirClassifierSymbol<*>>()
|
||||
|
||||
scope.processAllClassifiers {
|
||||
processClassifier(it)
|
||||
visitedClassifiers.add(it)
|
||||
}
|
||||
|
||||
// Scopes refer to inner classifiers
|
||||
// through maps indexed by names,
|
||||
// so only the last declaration is
|
||||
// observed when processing all
|
||||
// classifiers
|
||||
for (it in klass.declarationSymbols) {
|
||||
if (it is FirClassifierSymbol<*> && it !in visitedClassifiers) {
|
||||
processClassifier(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.fir.resolve.*
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.scopes.*
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.multipleDelegatesWithTheSameSignature
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
@@ -74,6 +75,12 @@ fun FirClassSymbol<*>.unsubstitutedScope(context: CheckerContext): FirTypeScope
|
||||
memberRequiredPhase = FirResolvePhase.STATUS,
|
||||
)
|
||||
|
||||
fun FirClassSymbol<*>.declaredMemberScope(context: CheckerContext): FirContainingNamesAwareScope =
|
||||
this.declaredMemberScope(
|
||||
context.sessionHolder.session,
|
||||
memberRequiredPhase = FirResolvePhase.STATUS,
|
||||
)
|
||||
|
||||
fun FirTypeRef.toClassLikeSymbol(session: FirSession): FirClassLikeSymbol<*>? {
|
||||
return coneTypeSafe<ConeClassLikeType>()?.toSymbol(session)
|
||||
}
|
||||
@@ -302,10 +309,15 @@ fun FirCallableDeclaration.isVisibleInClass(parentClass: FirClass): Boolean {
|
||||
return symbol.isVisibleInClass(parentClass.symbol)
|
||||
}
|
||||
|
||||
fun FirCallableSymbol<*>.isVisibleInClass(parentClassSymbol: FirClassSymbol<*>): Boolean {
|
||||
fun FirBasedSymbol<*>.isVisibleInClass(parentClassSymbol: FirClassSymbol<*>): Boolean {
|
||||
val classPackage = parentClassSymbol.classId.packageFqName
|
||||
val (visibility, packageName) = when (this) {
|
||||
is FirCallableSymbol<*> -> visibility to callableId.packageName
|
||||
is FirClassLikeSymbol<*> -> visibility to classId.packageFqName
|
||||
else -> return true
|
||||
}
|
||||
if (visibility == Visibilities.Private ||
|
||||
!visibility.visibleFromPackage(classPackage, callableId.packageName)
|
||||
!visibility.visibleFromPackage(classPackage, packageName)
|
||||
) return false
|
||||
if (
|
||||
visibility == Visibilities.Internal &&
|
||||
|
||||
+4
@@ -86,6 +86,10 @@ internal object FirRedeclarationPresenter {
|
||||
fun represent(it: FirVariableSymbol<*>) = buildString {
|
||||
appendRepresentationBeforeCallableId(it)
|
||||
appendRepresentation(it.callableId)
|
||||
|
||||
if (it is FirFieldSymbol) {
|
||||
append("#f")
|
||||
}
|
||||
}
|
||||
|
||||
fun represent(it: FirTypeAliasSymbol) = representClassLike(it)
|
||||
|
||||
+16
-9
@@ -18,9 +18,7 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirPackageMemberScope
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.PACKAGE_MEMBER
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.typeAliasForConstructor
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
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.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
|
||||
@@ -30,7 +28,7 @@ object FirConflictsDeclarationChecker : FirBasicDeclarationChecker() {
|
||||
is FirFile -> {
|
||||
val inspector = FirDeclarationCollector<FirBasedSymbol<*>>(context)
|
||||
checkFile(declaration, inspector, context)
|
||||
reportConflicts(reporter, context, inspector.declarationConflictingSymbols)
|
||||
reportConflicts(reporter, context, inspector.declarationConflictingSymbols, declaration)
|
||||
}
|
||||
is FirRegularClass -> {
|
||||
if (declaration.source?.kind !is KtFakeSourceElementKind) {
|
||||
@@ -38,7 +36,7 @@ object FirConflictsDeclarationChecker : FirBasicDeclarationChecker() {
|
||||
}
|
||||
val inspector = FirDeclarationCollector<FirBasedSymbol<*>>(context)
|
||||
inspector.collectClassMembers(declaration.symbol)
|
||||
reportConflicts(reporter, context, inspector.declarationConflictingSymbols)
|
||||
reportConflicts(reporter, context, inspector.declarationConflictingSymbols, declaration)
|
||||
}
|
||||
else -> {
|
||||
if (declaration.source?.kind !is KtFakeSourceElementKind && declaration is FirTypeParameterRefsOwner) {
|
||||
@@ -55,16 +53,24 @@ object FirConflictsDeclarationChecker : FirBasicDeclarationChecker() {
|
||||
reporter: DiagnosticReporter,
|
||||
context: CheckerContext,
|
||||
declarationConflictingSymbols: Map<FirBasedSymbol<*>, SmartSet<FirBasedSymbol<*>>>,
|
||||
container: FirDeclaration,
|
||||
) {
|
||||
declarationConflictingSymbols.forEach { (conflictingDeclaration, symbols) ->
|
||||
val typeAliasForConstructorSource = (conflictingDeclaration as? FirConstructorSymbol)?.typeAliasForConstructor?.source
|
||||
val source = typeAliasForConstructorSource ?: conflictingDeclaration.source
|
||||
val origin = conflictingDeclaration.origin
|
||||
val source = when {
|
||||
conflictingDeclaration !is FirCallableSymbol<*> -> conflictingDeclaration.source
|
||||
origin == FirDeclarationOrigin.Source -> conflictingDeclaration.source
|
||||
origin == FirDeclarationOrigin.Library -> return@forEach
|
||||
origin == FirDeclarationOrigin.Synthetic.TypeAliasConstructor -> typeAliasForConstructorSource
|
||||
else -> container.source
|
||||
}
|
||||
if (
|
||||
symbols.isEmpty() ||
|
||||
// For every implicit constructor there is a parent,
|
||||
// For every primary constructor there is a parent,
|
||||
// FirRegularClass declaration, and those clash too,
|
||||
// resulting in REDECLARATION.
|
||||
conflictingDeclaration.isImplicitConstructor && symbols.all { it.isImplicitConstructor }
|
||||
conflictingDeclaration.isPrimaryConstructor && symbols.all { it.isPrimaryConstructor }
|
||||
) return@forEach
|
||||
|
||||
val factory =
|
||||
@@ -83,7 +89,8 @@ object FirConflictsDeclarationChecker : FirBasicDeclarationChecker() {
|
||||
}
|
||||
}
|
||||
|
||||
private val FirBasedSymbol<*>.isImplicitConstructor get() = source?.kind is KtFakeSourceElementKind.ImplicitConstructor
|
||||
private val FirBasedSymbol<*>.isPrimaryConstructor: Boolean
|
||||
get() = this is FirConstructorSymbol && isPrimary || origin == FirDeclarationOrigin.Synthetic.TypeAliasConstructor
|
||||
|
||||
private fun checkFile(file: FirFile, inspector: FirDeclarationCollector<FirBasedSymbol<*>>, context: CheckerContext) {
|
||||
val packageMemberScope: FirPackageMemberScope = context.sessionHolder.scopeSession.getOrBuild(file.packageFqName, PACKAGE_MEMBER) {
|
||||
|
||||
+51
-3
@@ -5,9 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.scopes
|
||||
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
abstract class FirContainingNamesAwareScope : FirScope() {
|
||||
@@ -35,6 +33,56 @@ fun FirContainingNamesAwareScope.processAllCallables(processor: (FirCallableSymb
|
||||
}
|
||||
}
|
||||
|
||||
fun FirContainingNamesAwareScope.processAllClassifiers(processor: (FirClassifierSymbol<*>) -> Unit) {
|
||||
for (name in getClassifierNames()) {
|
||||
processClassifiersByName(name, processor)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : FirCallableSymbol<*>> collectLeafCallablesByName(
|
||||
name: Name,
|
||||
processCallablesByName: (Name, (T) -> Unit) -> Unit,
|
||||
crossinline processDirectlyOverriddenCallables: (T, (T) -> ProcessorAction) -> Unit,
|
||||
): List<T> {
|
||||
val collected = mutableSetOf<T>()
|
||||
val bases = mutableSetOf<T>()
|
||||
|
||||
processCallablesByName(name) { function ->
|
||||
processDirectlyOverriddenCallables(function) {
|
||||
bases.add(it)
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
|
||||
if (function !in bases) {
|
||||
collected.add(function)
|
||||
}
|
||||
}
|
||||
|
||||
return collected.filter { it !in bases }
|
||||
}
|
||||
|
||||
fun FirTypeScope.collectLeafFunctionsByName(name: Name): List<FirNamedFunctionSymbol> =
|
||||
collectLeafCallablesByName(name, ::processFunctionsByName, ::processDirectlyOverriddenFunctions)
|
||||
|
||||
fun FirTypeScope.collectLeafPropertiesByName(name: Name): List<FirVariableSymbol<*>> =
|
||||
collectLeafCallablesByName(name, ::processPropertiesByName) { variable, process ->
|
||||
if (variable is FirPropertySymbol) {
|
||||
processDirectlyOverriddenProperties(variable, process)
|
||||
}
|
||||
}
|
||||
|
||||
fun FirTypeScope.collectLeafFunctions(): List<FirNamedFunctionSymbol> = buildList {
|
||||
for (name in getCallableNames()) {
|
||||
this += collectLeafFunctionsByName(name)
|
||||
}
|
||||
}
|
||||
|
||||
fun FirTypeScope.collectLeafProperties(): List<FirVariableSymbol<*>> = buildList {
|
||||
for (name in getCallableNames()) {
|
||||
this += collectLeafPropertiesByName(name)
|
||||
}
|
||||
}
|
||||
|
||||
fun FirContainingNamesAwareScope.collectAllProperties(): Collection<FirVariableSymbol<*>> {
|
||||
return mutableListOf<FirVariableSymbol<*>>().apply {
|
||||
processAllProperties(this::add)
|
||||
|
||||
Reference in New Issue
Block a user