FIR LL: fix KtDeclarationAndFirDeclarationEqualityChecker
1. Nullable function type is not rendered correctly.
2. Bounds of type parameters are not considered during comparison, this
would incorrectly treat different declarations as the same, for
example
```
fun <C: List<String>> C.foo() {}
fun <C: Set<String>> C.foo() {}
```
This commit is contained in:
committed by
Ilya Kirillov
parent
f23256bf49
commit
8bec5cec61
+44
-8
@@ -20,11 +20,13 @@ import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScopeProvider
|
||||
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirImplicitNullableAnyTypeRef
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtTypeReference
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
// TODO replace with structural type comparison?
|
||||
object KtDeclarationAndFirDeclarationEqualityChecker {
|
||||
@@ -40,15 +42,28 @@ object KtDeclarationAndFirDeclarationEqualityChecker {
|
||||
return false
|
||||
}
|
||||
if (fir is FirFunction) {
|
||||
if (fir.valueParameters.size != psi.valueParameters.size) return false
|
||||
fir.valueParameters.zip(psi.valueParameters) { expectedParameter, candidateParameter ->
|
||||
if (expectedParameter.name.toString() != candidateParameter.name) return false
|
||||
if (expectedParameter.isVararg != candidateParameter.isVarArg) return false
|
||||
val candidateParameterType = candidateParameter.typeReference ?: return false
|
||||
if (!typeParametersMatch(fir, psi) || !valueParametersMatch(fir, psi)) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun typeParametersMatch(firFunction: FirCallableDeclaration, psiFunction: KtCallableDeclaration): Boolean {
|
||||
if (firFunction.typeParameters.size != psiFunction.typeParameters.size) return false
|
||||
val boundsByName = psiFunction.typeConstraints.groupBy { it.subjectTypeParameterName?.getReferencedName() }
|
||||
firFunction.typeParameters.zip(psiFunction.typeParameters) { expectedTypeParameter, candidateTypeParameter ->
|
||||
if (expectedTypeParameter.symbol.name.toString() != candidateTypeParameter.name) return false
|
||||
val candidateBounds = mutableListOf<KtTypeReference>()
|
||||
candidateBounds.addIfNotNull(candidateTypeParameter.extendsBound)
|
||||
boundsByName[candidateTypeParameter.name]?.forEach {
|
||||
candidateBounds.addIfNotNull(it.boundTypeReference)
|
||||
}
|
||||
val expectedBounds = expectedTypeParameter.symbol.resolvedBounds.filter { it !is FirImplicitNullableAnyTypeRef }
|
||||
if (candidateBounds.size != expectedBounds.size) return false
|
||||
expectedBounds.zip(candidateBounds) { expectedBound, candidateBound ->
|
||||
if (!isTheSameTypes(
|
||||
candidateParameterType,
|
||||
expectedParameter.returnTypeRef,
|
||||
isVararg = expectedParameter.isVararg
|
||||
candidateBound,
|
||||
expectedBound,
|
||||
isVararg = false
|
||||
)
|
||||
) {
|
||||
return false
|
||||
@@ -58,6 +73,24 @@ object KtDeclarationAndFirDeclarationEqualityChecker {
|
||||
return true
|
||||
}
|
||||
|
||||
private fun valueParametersMatch(firFunction: FirFunction, psiFunction: KtCallableDeclaration): Boolean {
|
||||
if (firFunction.valueParameters.size != psiFunction.valueParameters.size) return false
|
||||
firFunction.valueParameters.zip(psiFunction.valueParameters) { expectedParameter, candidateParameter ->
|
||||
if (expectedParameter.name.toString() != candidateParameter.name) return false
|
||||
if (expectedParameter.isVararg != candidateParameter.isVarArg) return false
|
||||
val candidateParameterType = candidateParameter.typeReference ?: return false
|
||||
if (!isTheSameTypes(
|
||||
candidateParameterType,
|
||||
expectedParameter.returnTypeRef,
|
||||
isVararg = expectedParameter.isVararg
|
||||
)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun FirTypeRef.renderTypeAsKotlinType(isVararg: Boolean = false): String {
|
||||
val rendered = when (this) {
|
||||
@@ -89,6 +122,9 @@ object KtDeclarationAndFirDeclarationEqualityChecker {
|
||||
if (parameters.isNotEmpty()) {
|
||||
append(parameters.joinToString(prefix = "<", postfix = ">") { it.renderTypeAsKotlinType() })
|
||||
}
|
||||
if (isMarkedNullable) {
|
||||
append("?")
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> error("Invalid type reference $this")
|
||||
|
||||
Reference in New Issue
Block a user