[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:
Nikolay Lunyak
2023-09-20 16:52:32 +03:00
committed by Space Team
parent 973248f432
commit 4e58715760
24 changed files with 278 additions and 129 deletions
@@ -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 &&
@@ -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)
@@ -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) {
@@ -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)