[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
+11
-5
@@ -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
|
||||
|
||||
+12
-5
@@ -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
@@ -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()
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user