[FIR] Support synthetic property setters

This commit is contained in:
Mikhail Glukhikh
2020-02-05 16:41:52 +03:00
parent a9ba94cf0e
commit cc0e39ebca
7 changed files with 53 additions and 24 deletions
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.symbols.SyntheticSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
@@ -38,24 +38,41 @@ class FirSyntheticPropertiesScope(
val synthetic: MutableMap<FirCallableSymbol<*>, FirVariableSymbol<*>> = mutableMapOf()
private fun checkGetAndCreateSynthetic(
name: Name,
symbol: FirFunctionSymbol<*>,
propertyName: Name,
getterName: Name,
getterSymbol: FirFunctionSymbol<*>,
processor: (FirVariableSymbol<*>) -> Unit
) {
val fir = symbol.fir as? FirSimpleFunction ?: return
val getter = getterSymbol.fir as? FirSimpleFunction ?: return
if (fir.typeParameters.isNotEmpty()) return
if (fir.valueParameters.isNotEmpty()) return
if (fir.isStatic) return
if (fir.returnTypeRef.coneTypeSafe<ConeClassLikeType>()?.lookupTag?.classId == StandardClassIds.Unit) return
if (getter.typeParameters.isNotEmpty()) return
if (getter.valueParameters.isNotEmpty()) return
if (getter.isStatic) return
val getterReturnType = (getter.returnTypeRef as? FirResolvedTypeRef)?.type
if ((getterReturnType as? ConeClassLikeType)?.lookupTag?.classId == StandardClassIds.Unit) return
var matchingSetter: FirSimpleFunction? = null
if (getterReturnType != null) {
val setterName = setterNameByGetterName(getterName)
baseScope.processFunctionsByName(setterName, fun(setterSymbol: FirFunctionSymbol<*>) {
if (matchingSetter != null) return
val setter = setterSymbol.fir as? FirSimpleFunction ?: return
val parameter = setter.valueParameters.singleOrNull() ?: return
if (setter.typeParameters.isNotEmpty() || setter.isStatic) return
val parameterType = (parameter.returnTypeRef as? FirResolvedTypeRef)?.type ?: return
if (parameterType != getterReturnType) return
matchingSetter = setter
})
}
val property = FirSyntheticProperty(
session, name,
session, propertyName,
symbol = SyntheticPropertySymbol(
accessorId = symbol.callableId,
callableId = CallableId(symbol.callableId.packageName, symbol.callableId.className, name)
accessorId = getterSymbol.callableId,
callableId = CallableId(getterSymbol.callableId.packageName, getterSymbol.callableId.className, propertyName)
),
delegateGetter = fir
delegateGetter = getter,
delegateSetter = matchingSetter
)
processor(property.symbol)
}
@@ -64,7 +81,7 @@ class FirSyntheticPropertiesScope(
val getterNames = possibleGetterNamesByPropertyName(name)
for (getterName in getterNames) {
baseScope.processFunctionsByName(getterName) {
checkGetAndCreateSynthetic(name, it, processor)
checkGetAndCreateSynthetic(name, getterName, it, processor)
}
}
}
@@ -84,6 +101,16 @@ class FirSyntheticPropertiesScope(
}
}
fun setterNameByGetterName(name: Name): Name {
val identifier = name.identifier
val prefix = when {
identifier.startsWith("get") -> "get"
identifier.startsWith("is") -> "is"
else -> throw IllegalArgumentException()
}
return Name.identifier("set" + identifier.removePrefix(prefix))
}
private const val GETTER_PREFIX = "get"
private const val IS_PREFIX = "is"
@@ -18,5 +18,5 @@ public class B {
// FILE: main.kt
fun test(b: B) {
b.<!VARIABLE_EXPECTED!>text<!> += ""
b.text += ""
}
@@ -1,4 +1,4 @@
FILE: main.kt
public final fun test(b: R|B|): R|kotlin/Unit| {
<Variable expected># = R|<local>/b|.R|/B.text|.R|kotlin/String.plus|(String())
R|/B.text| = R|<local>/b|.R|/B.text|.R|kotlin/String.plus|(String())
}
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
class FirSyntheticProperty(
class FirSyntheticProperty private constructor(
override val session: FirSession,
override val returnTypeRef: FirTypeRef,
override val name: Name,
@@ -41,12 +41,14 @@ class FirSyntheticProperty(
session: FirSession,
name: Name,
symbol: FirAccessorSymbol,
delegateGetter: FirSimpleFunction
delegateGetter: FirSimpleFunction,
delegateSetter: FirSimpleFunction? = null
) : this(
session, delegateGetter.returnTypeRef, name, false, symbol,
FirDeclarationStatusImpl(delegateGetter.visibility, delegateGetter.modality),
delegateGetter.resolvePhase,
FirSyntheticPropertyAccessor(delegateGetter, isGetter = true)
session, delegateGetter.returnTypeRef, name, isVar = delegateSetter != null, symbol = symbol,
status = FirDeclarationStatusImpl(delegateGetter.visibility, delegateGetter.modality),
resolvePhase = delegateGetter.resolvePhase,
getter = FirSyntheticPropertyAccessor(delegateGetter, isGetter = true),
setter = delegateSetter?.let { FirSyntheticPropertyAccessor(it, isGetter = false) }
)
override val source: FirSourceElement?
@@ -1,7 +1,7 @@
// FILE: KotlinFile.kt
fun foo(javaClass: JavaClass) {
javaClass.url = javaClass.url + "/"
javaClass.<!VARIABLE_EXPECTED!>htmlFile<!> += "1"
javaClass.htmlFile += "1"
javaClass.<!UNRESOLVED_REFERENCE!>URL<!>
javaClass.<!UNRESOLVED_REFERENCE!>uRL<!>
@@ -1,6 +1,6 @@
// FILE: KotlinFile.kt
fun foo(javaClass: JavaClass<String>) {
javaClass.<!VARIABLE_EXPECTED!>something<!> += "x"
javaClass.something += "x"
}
// FILE: JavaClass.java
@@ -22,7 +22,7 @@ fun foo(k: KotlinClass) {
useString(k.something3)
k.setSomething4("")
k.<!VARIABLE_EXPECTED!>something4<!> += ""
k.something4 += ""
k.<!INAPPLICABLE_CANDIDATE!>setSomething4<!>(null)
k.something4 = null