[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)
@@ -0,0 +1,55 @@
open class Final {
fun foo() {}
val bar: Int = 0
var qux: Int = 0
}
open class Derived : Final()
interface IFoo {
fun foo()
}
class CFoo : IFoo {
override fun foo() {}
}
interface IBar {
val bar: Int
}
class CBar : IBar {
override val bar: Int get() = 0
}
interface IQux {
val qux: Int
}
class CQux : IQux {
override val qux: Int get() = 0
}
interface IBarT<T> {
val bar: T
}
class CBarT<T> : IBarT<T> {
override val bar: T get() = null!!
}
<!OVERRIDING_FINAL_MEMBER_BY_DELEGATION!>class <!CONFLICTING_OVERLOADS, CONFLICTING_OVERLOADS!>Test1<!><!> : Final(), IFoo by CFoo()
<!OVERRIDING_FINAL_MEMBER_BY_DELEGATION!>class <!REDECLARATION, REDECLARATION!>Test2<!><!> : Final(), IBar by CBar()
<!OVERRIDING_FINAL_MEMBER_BY_DELEGATION, VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION!>class <!REDECLARATION, REDECLARATION!>Test3<!><!> : Final(), IQux by CQux()
<!OVERRIDING_FINAL_MEMBER_BY_DELEGATION!>class <!CONFLICTING_OVERLOADS, CONFLICTING_OVERLOADS!>Test4<!><!> : Derived(), IFoo by CFoo()
<!OVERRIDING_FINAL_MEMBER_BY_DELEGATION!>class <!REDECLARATION, REDECLARATION!>Test5<!><!> : Derived(), IBar by CBar()
<!OVERRIDING_FINAL_MEMBER_BY_DELEGATION, VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION!>class <!REDECLARATION, REDECLARATION!>Test6<!><!> : Derived(), IQux by CQux()
<!OVERRIDING_FINAL_MEMBER_BY_DELEGATION!>class <!REDECLARATION, REDECLARATION!>Test7<!><!> : Final(), IBarT<Int> by CBarT<Int>()
<!OVERRIDING_FINAL_MEMBER_BY_DELEGATION!>class <!REDECLARATION, REDECLARATION!>Test8<!><!> : Final(), IBarT<Int> by <!TYPE_MISMATCH!>CBar()<!>
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
open class Final {
fun foo() {}
val bar: Int = 0
@@ -1,3 +1,5 @@
enum class A {
name
enum class <!REDECLARATION, REDECLARATION!>A<!> {
<!REDECLARATION!>name<!>,
<!REDECLARATION!>ordinal<!>,
<!DEPRECATED_DECLARATION_OF_ENUM_ENTRY!>entries,<!>
}
@@ -1,3 +1,5 @@
enum class A {
<!REDECLARATION!>name<!>
<!REDECLARATION!>name<!>,
<!REDECLARATION!>ordinal<!>,
<!DEPRECATED_DECLARATION_OF_ENUM_ENTRY!>entries,<!>
}
@@ -3,6 +3,10 @@ package
public final enum class A : kotlin.Enum<A> {
enum entry name
enum entry ordinal
enum entry entries
private constructor A()
@kotlin.internal.IntrinsicConstEvaluation public final override /*1*/ /*fake_override*/ val name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
@@ -1,14 +0,0 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER -TOPLEVEL_TYPEALIASES_ONLY
class C(val x: Int)
<!CONFLICTING_OVERLOADS!>typealias CC = C<!>
<!CONFLICTING_OVERLOADS!>fun CC(x: Int)<!> = x
class Outer {
class C(val x: Int)
typealias CC = C
fun CC(x: Int) = x
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER -TOPLEVEL_TYPEALIASES_ONLY
class C(val x: Int)
@@ -1,14 +0,0 @@
// ISSUE: KT-57100
// WITH_STDLIB
interface C07I01{
fun some() {}
}
interface C07I02{
suspend fun some() {}
}
class C07C01: C07I01, C07I02 {
override fun some() {}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// ISSUE: KT-57100
// WITH_STDLIB
@@ -10,5 +11,5 @@ interface C07I02{
}
class C07C01: C07I01, C07I02 {
<!CONFLICTING_OVERLOADS!>override fun some()<!> {}
override fun some() {}
}
@@ -1,6 +0,0 @@
interface AsyncVal { suspend fun getVal(): Int = 1}
interface SyncVal { fun getVal(): Int = 1 }
<!CONFLICTING_INHERITED_MEMBERS, MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED!>class MixSuspend<!> : AsyncVal, SyncVal {
}
@@ -1,6 +1,7 @@
// FIR_IDENTICAL
interface AsyncVal { suspend fun getVal(): Int = 1}
interface SyncVal { fun getVal(): Int = 1 }
<!CONFLICTING_INHERITED_MEMBERS!>class MixSuspend<!> : AsyncVal, SyncVal {
<!CONFLICTING_INHERITED_MEMBERS, MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED!>class MixSuspend<!> : AsyncVal, SyncVal {
}
@@ -1,37 +0,0 @@
// FILE: main.kt
interface A {
suspend fun foo()
fun bar()
}
interface B : A {
override fun foo() {
}
override suspend fun bar() {
}
}
interface C : A {
suspend override fun foo() {
}
override fun bar() {
}
}
<!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>class D<!> : J {
suspend override fun foo() {
}
}
// FILE: J.java
public interface J extends A {
Object foo(kotlin.coroutines.Continuation<kotlin.Unit> y);
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// FILE: main.kt
interface A {
suspend fun foo()
@@ -5,11 +6,11 @@ interface A {
}
interface B : A {
<!CONFLICTING_OVERLOADS!><!NOTHING_TO_OVERRIDE!>override<!> fun foo()<!> {
override fun foo() {
}
<!CONFLICTING_OVERLOADS!><!NOTHING_TO_OVERRIDE!>override<!> suspend fun bar()<!> {
override suspend fun bar() {
}
}
@@ -18,6 +18,8 @@ import org.jetbrains.kotlin.fir.java.declarations.FirJavaField
import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod
import org.jetbrains.kotlin.fir.java.declarations.buildJavaMethod
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.scopes.collectAllFunctions
import org.jetbrains.kotlin.fir.scopes.impl.FirClassDeclaredMemberScope
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
@@ -37,26 +39,30 @@ class GetterGenerator(session: FirSession) : FirDeclarationGenerationExtension(s
private val lombokService: LombokService
get() = session.lombokService
private val cache: FirCache<FirClassSymbol<*>, Map<Name, FirJavaMethod>?, Nothing?> =
session.firCachesFactory.createCache(::createGetters)
private val cache: FirCache<Pair<FirClassSymbol<*>, FirClassDeclaredMemberScope?>, Map<Name, FirJavaMethod>?, Nothing?> =
session.firCachesFactory.createCache(uncurry(::createGetters))
override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>, context: MemberGenerationContext): Set<Name> {
if (!classSymbol.isSuitableJavaClass()) return emptySet()
return cache.getValue(classSymbol)?.keys ?: emptySet()
return cache.getValue(classSymbol to context.declaredScope)?.keys ?: emptySet()
}
override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List<FirNamedFunctionSymbol> {
val owner = context?.owner
if (owner == null || !owner.isSuitableJavaClass()) return emptyList()
val getter = cache.getValue(owner)?.get(callableId.callableName) ?: return emptyList()
val getter = cache.getValue(owner to context.declaredScope)?.get(callableId.callableName) ?: return emptyList()
return listOf(getter.symbol)
}
private fun createGetters(classSymbol: FirClassSymbol<*>): Map<Name, FirJavaMethod>? {
private fun createGetters(classSymbol: FirClassSymbol<*>, declaredScope: FirClassDeclaredMemberScope?): Map<Name, FirJavaMethod>? {
val fieldsWithGetter = computeFieldsWithGetter(classSymbol) ?: return null
val globalAccessors = lombokService.getAccessors(classSymbol)
val explicitlyDeclaredFunctions = declaredScope?.collectAllFunctions()?.associateBy { it.name }.orEmpty()
return fieldsWithGetter.mapNotNull { (field, getterInfo) ->
val getterName = computeGetterName(field, getterInfo, globalAccessors) ?: return@mapNotNull null
if (explicitlyDeclaredFunctions[getterName]?.valueParameterSymbols?.isEmpty() == true) {
return@mapNotNull null
}
val function = buildJavaMethod {
moduleData = field.moduleData
returnTypeRef = field.returnTypeRef
@@ -20,6 +20,8 @@ import org.jetbrains.kotlin.fir.java.declarations.FirJavaMethod
import org.jetbrains.kotlin.fir.java.declarations.buildJavaMethod
import org.jetbrains.kotlin.fir.java.declarations.buildJavaValueParameter
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.scopes.collectAllFunctions
import org.jetbrains.kotlin.fir.scopes.impl.FirClassDeclaredMemberScope
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
@@ -40,18 +42,18 @@ class SetterGenerator(session: FirSession) : FirDeclarationGenerationExtension(s
private val lombokService: LombokService
get() = session.lombokService
private val cache: FirCache<FirClassSymbol<*>, Map<Name, FirJavaMethod>?, Nothing?> =
session.firCachesFactory.createCache(::createSetters)
private val cache: FirCache<Pair<FirClassSymbol<*>, FirClassDeclaredMemberScope?>, Map<Name, FirJavaMethod>?, Nothing?> =
session.firCachesFactory.createCache(uncurry(::createSetters))
override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>, context: MemberGenerationContext): Set<Name> {
if (!classSymbol.isSuitableForSetters()) return emptySet()
return cache.getValue(classSymbol)?.keys ?: emptySet()
return cache.getValue(classSymbol to context.declaredScope)?.keys ?: emptySet()
}
override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List<FirNamedFunctionSymbol> {
val owner = context?.owner
if (owner == null || !owner.isSuitableForSetters()) return emptyList()
val getter = cache.getValue(owner)?.get(callableId.callableName) ?: return emptyList()
val getter = cache.getValue(owner to context.declaredScope)?.get(callableId.callableName) ?: return emptyList()
return listOf(getter.symbol)
}
@@ -59,12 +61,17 @@ class SetterGenerator(session: FirSession) : FirDeclarationGenerationExtension(s
return isSuitableJavaClass() && classKind != ClassKind.ENUM_CLASS
}
private fun createSetters(classSymbol: FirClassSymbol<*>): Map<Name, FirJavaMethod>? {
private fun createSetters(classSymbol: FirClassSymbol<*>, declaredScope: FirClassDeclaredMemberScope?): Map<Name, FirJavaMethod>? {
val fieldsWithSetter = computeFieldsWithSetters(classSymbol) ?: return null
val globalAccessors = lombokService.getAccessors(classSymbol)
val explicitlyDeclaredFunctions = declaredScope?.collectAllFunctions()?.associateBy { it.name }.orEmpty()
return fieldsWithSetter.mapNotNull { (field, setterInfo) ->
val accessors = lombokService.getAccessorsIfAnnotated(field.symbol) ?: globalAccessors
val setterName = computeSetterName(field, setterInfo, accessors) ?: return@mapNotNull null
val existing = explicitlyDeclaredFunctions[setterName]
if (existing != null && existing.valueParameterSymbols.size == 1) {
return@mapNotNull null
}
val function = buildJavaMethod {
moduleData = field.moduleData
returnTypeRef = if (accessors.chain) {
@@ -81,3 +81,5 @@ private fun sameSignature(a: FirFunction, b: FirFunction): Boolean {
bVararg && aSize >= (bSize - 1) ||
aSize == bSize
}
internal inline fun <A, B, C> uncurry(crossinline f: (A, B) -> C): (Pair<A, B>) -> C = { (a, b) -> f(a, b) }
+12 -4
View File
@@ -39,6 +39,11 @@ public class ClashTest extends SuperClass {
return human;
}
private int score;
public void setScore(int score, int times) {
}
static void test() {
val obj = new ClashTest();
@@ -82,11 +87,14 @@ class KotlinChildClass : ClashTest() {
fun test() {
val obj = ClashTest()
obj.<!OVERLOAD_RESOLUTION_AMBIGUITY!>getAge<!>()
obj.getAge()
//thats shouldn't work because lombok doesn't generate clashing method
obj.setAge(41)
obj.<!OVERLOAD_RESOLUTION_AMBIGUITY!>age<!> = 12
val age = obj.<!OVERLOAD_RESOLUTION_AMBIGUITY!>age<!>
obj.setAge(<!ARGUMENT_TYPE_MISMATCH!>41<!>)
obj.<!VAL_REASSIGNMENT!>age<!> = 12
val age = obj.age
obj.setScore(41)
obj.score = 12
obj.getName()
+8
View File
@@ -39,6 +39,11 @@ public class ClashTest extends SuperClass {
return human;
}
private int score;
public void setScore(int score, int times) {
}
static void test() {
val obj = new ClashTest();
@@ -88,6 +93,9 @@ fun test() {
<!VAL_REASSIGNMENT!>obj.age<!> = 12
val age = obj.age
obj.setScore(41)
obj.score = 12
obj.getName()
obj.setName("Al")
+13
View File
@@ -8,10 +8,12 @@ public open class ChildClass : ClashTest {
invisible_fake final override /*1*/ /*fake_override*/ var age: kotlin.Int
invisible_fake final override /*1*/ /*fake_override*/ var human: kotlin.Boolean
invisible_fake final override /*1*/ /*fake_override*/ var name: kotlin.String!
invisible_fake final override /*1*/ /*fake_override*/ var score: kotlin.Int
invisible_fake final override /*1*/ /*fake_override*/ var toOverride: kotlin.Int!
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun getAge(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun getName(): kotlin.String!
public open override /*1*/ /*fake_override*/ fun getScore(): kotlin.Int
@java.lang.Override public open override /*1*/ fun getToOverride(): kotlin.Int!
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun isHuman(): kotlin.Boolean
@@ -19,6 +21,8 @@ public open class ChildClass : ClashTest {
public open override /*1*/ /*fake_override*/ fun setAge(/*0*/ age: kotlin.String!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun setHuman(/*0*/ human: kotlin.Boolean): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun setName(/*0*/ name: kotlin.String!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun setScore(/*0*/ score: kotlin.Int): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun setScore(/*0*/ score: kotlin.Int, /*1*/ times: kotlin.Int): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun setToOverride(/*0*/ toOverride: kotlin.Int!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@@ -31,10 +35,12 @@ public open class ChildClass : ClashTest {
private final var age: kotlin.Int
private final var human: kotlin.Boolean
private final var name: kotlin.String!
private final var score: kotlin.Int
private final var toOverride: kotlin.Int!
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open fun getAge(): kotlin.Int
public open /*synthesized*/ fun getName(): kotlin.String!
public open /*synthesized*/ fun getScore(): kotlin.Int
public open /*synthesized*/ fun getToOverride(): kotlin.Int!
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open /*synthesized*/ fun isHuman(): kotlin.Boolean
@@ -42,6 +48,8 @@ public open class ChildClass : ClashTest {
public open fun setAge(/*0*/ age: kotlin.String!): kotlin.Unit
public open /*synthesized*/ fun setHuman(/*0*/ human: kotlin.Boolean): kotlin.Unit
public open override /*1*/ /*synthesized*/ fun setName(/*0*/ name: kotlin.String!): kotlin.Unit
public open /*synthesized*/ fun setScore(/*0*/ score: kotlin.Int): kotlin.Unit
public open fun setScore(/*0*/ score: kotlin.Int, /*1*/ times: kotlin.Int): kotlin.Unit
public open /*synthesized*/ fun setToOverride(/*0*/ toOverride: kotlin.Int!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@@ -54,10 +62,12 @@ public final class KotlinChildClass : ClashTest {
invisible_fake final override /*1*/ /*fake_override*/ var age: kotlin.Int
invisible_fake final override /*1*/ /*fake_override*/ var human: kotlin.Boolean
invisible_fake final override /*1*/ /*fake_override*/ var name: kotlin.String!
invisible_fake final override /*1*/ /*fake_override*/ var score: kotlin.Int
invisible_fake final override /*1*/ /*fake_override*/ var toOverride: kotlin.Int!
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun getAge(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun getName(): kotlin.String!
public open override /*1*/ /*fake_override*/ fun getScore(): kotlin.Int
public open override /*1*/ fun getToOverride(): kotlin.Int?
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun isHuman(): kotlin.Boolean
@@ -65,6 +75,8 @@ public final class KotlinChildClass : ClashTest {
public open override /*1*/ /*fake_override*/ fun setAge(/*0*/ age: kotlin.String!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun setHuman(/*0*/ human: kotlin.Boolean): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun setName(/*0*/ name: kotlin.String!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun setScore(/*0*/ score: kotlin.Int): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun setScore(/*0*/ score: kotlin.Int, /*1*/ times: kotlin.Int): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun setToOverride(/*0*/ toOverride: kotlin.Int!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -76,3 +88,4 @@ public open class SuperClass {
public open fun setName(/*0*/ name: kotlin.String!): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}