[FIR] Refactor FirTypeResolverImpl.resolveUserType
Fix IndexOutOfBoundsException on an attempt to cast an element to inner class with type parameter ^KT-60921 Fixed
This commit is contained in:
committed by
Space Team
parent
5cd9afbf73
commit
42e5e43302
+6
@@ -13833,6 +13833,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
|
|||||||
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/bareTypesComplex.kt");
|
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/bareTypesComplex.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("castAnElementToInnerClassWithTypeParameter.kt")
|
||||||
|
public void testCastAnElementToInnerClassWithTypeParameter() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/castAnElementToInnerClassWithTypeParameter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("checkBoundsOuter.kt")
|
@TestMetadata("checkBoundsOuter.kt")
|
||||||
public void testCheckBoundsOuter() throws Exception {
|
public void testCheckBoundsOuter() throws Exception {
|
||||||
|
|||||||
+6
@@ -13833,6 +13833,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
|
|||||||
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/bareTypesComplex.kt");
|
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/bareTypesComplex.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("castAnElementToInnerClassWithTypeParameter.kt")
|
||||||
|
public void testCastAnElementToInnerClassWithTypeParameter() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/castAnElementToInnerClassWithTypeParameter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("checkBoundsOuter.kt")
|
@TestMetadata("checkBoundsOuter.kt")
|
||||||
public void testCheckBoundsOuter() throws Exception {
|
public void testCheckBoundsOuter() throws Exception {
|
||||||
|
|||||||
+6
@@ -13833,6 +13833,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
|||||||
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/bareTypesComplex.kt");
|
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/bareTypesComplex.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("castAnElementToInnerClassWithTypeParameter.kt")
|
||||||
|
public void testCastAnElementToInnerClassWithTypeParameter() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/castAnElementToInnerClassWithTypeParameter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("checkBoundsOuter.kt")
|
@TestMetadata("checkBoundsOuter.kt")
|
||||||
public void testCheckBoundsOuter() throws Exception {
|
public void testCheckBoundsOuter() throws Exception {
|
||||||
|
|||||||
+6
@@ -13839,6 +13839,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
|||||||
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/bareTypesComplex.kt");
|
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/bareTypesComplex.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("castAnElementToInnerClassWithTypeParameter.kt")
|
||||||
|
public void testCastAnElementToInnerClassWithTypeParameter() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/castAnElementToInnerClassWithTypeParameter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("checkBoundsOuter.kt")
|
@TestMetadata("checkBoundsOuter.kt")
|
||||||
public void testCheckBoundsOuter() throws Exception {
|
public void testCheckBoundsOuter() throws Exception {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.fir.types.*
|
|||||||
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
|
import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef
|
||||||
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||||
|
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
||||||
import org.jetbrains.kotlin.fir.utils.exceptions.withFirEntry
|
import org.jetbrains.kotlin.fir.utils.exceptions.withFirEntry
|
||||||
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
import org.jetbrains.kotlin.fir.visitors.FirTransformer
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
@@ -625,3 +626,6 @@ fun FirNamedReferenceWithCandidate.toErrorReference(diagnostic: ConeDiagnostic):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val FirTypeParameterSymbol.defaultType: ConeTypeParameterType
|
||||||
|
get() = ConeTypeParameterTypeImpl(toLookupTag(), isNullable = false)
|
||||||
+102
-166
@@ -5,12 +5,11 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.resolve.providers.impl
|
package org.jetbrains.kotlin.fir.resolve.providers.impl
|
||||||
|
|
||||||
import org.jetbrains.kotlin.KtSourceElement
|
|
||||||
import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind
|
import org.jetbrains.kotlin.builtins.functions.FunctionTypeKind
|
||||||
import org.jetbrains.kotlin.fir.*
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirOuterClassTypeParameterRef
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
|
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.utils.isInner
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
||||||
import org.jetbrains.kotlin.fir.diagnostics.*
|
import org.jetbrains.kotlin.fir.diagnostics.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||||
@@ -21,13 +20,11 @@ import org.jetbrains.kotlin.fir.resolve.calls.ResolutionDiagnostic
|
|||||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.ScopeClassDeclaration
|
import org.jetbrains.kotlin.fir.resolve.transformers.ScopeClassDeclaration
|
||||||
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||||
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError
|
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError
|
||||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||||
@@ -206,7 +203,6 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
|
|||||||
containerDeclaration: FirDeclaration?,
|
containerDeclaration: FirDeclaration?,
|
||||||
isOperandOfIsOperator: Boolean
|
isOperandOfIsOperator: Boolean
|
||||||
): ConeKotlinType {
|
): ConeKotlinType {
|
||||||
|
|
||||||
val (symbol, substitutor) = when (result) {
|
val (symbol, substitutor) = when (result) {
|
||||||
is TypeResolutionResult.Resolved -> {
|
is TypeResolutionResult.Resolved -> {
|
||||||
result.typeCandidate.symbol to result.typeCandidate.substitutor
|
result.typeCandidate.symbol to result.typeCandidate.substitutor
|
||||||
@@ -215,87 +211,13 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
|
|||||||
TypeResolutionResult.Unresolved -> null to null
|
TypeResolutionResult.Unresolved -> null to null
|
||||||
}
|
}
|
||||||
|
|
||||||
val allTypeArguments = mutableListOf<ConeTypeProjection>()
|
|
||||||
var typeArgumentsCount = 0
|
|
||||||
|
|
||||||
val qualifier = typeRef.qualifier
|
val qualifier = typeRef.qualifier
|
||||||
for (qualifierIndex in qualifier.size - 1 downTo 0) {
|
val allTypeArguments =
|
||||||
val qualifierTypeArguments = qualifier[qualifierIndex].typeArgumentList.typeArguments
|
qualifier.reversed().flatMap { it.typeArgumentList.typeArguments }.mapTo(mutableListOf()) { it.toConeTypeProjection() }
|
||||||
|
|
||||||
for (qualifierTypeArgument in qualifierTypeArguments) {
|
if (symbol is FirClassLikeSymbol<*> && !isPossibleBareType(areBareTypesAllowed, allTypeArguments)) {
|
||||||
allTypeArguments.add(qualifierTypeArgument.toConeTypeProjection())
|
matchQualifierPartsAndClasses(symbol, qualifier, typeRef)?.let { return it }
|
||||||
typeArgumentsCount++
|
allTypeArguments.addImplicitTypeArgumentsOrReturnError(symbol, topContainer, substitutor)?.let { return it }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (symbol is FirClassLikeSymbol<*>) {
|
|
||||||
val isPossibleBareType = areBareTypesAllowed && allTypeArguments.isEmpty()
|
|
||||||
if (!isPossibleBareType) {
|
|
||||||
val actualSubstitutor = substitutor ?: ConeSubstitutor.Empty
|
|
||||||
|
|
||||||
val originalTypeParameters = symbol.fir.typeParameters
|
|
||||||
|
|
||||||
val (typeParametersAlignedToQualifierParts, outerDeclarations) = getClassesAlignedToQualifierParts(
|
|
||||||
symbol,
|
|
||||||
qualifier,
|
|
||||||
session
|
|
||||||
)
|
|
||||||
|
|
||||||
val actualTypeParametersCount = symbol.typeParameterSymbols.size
|
|
||||||
|
|
||||||
for ((typeParameterIndex, typeParameter) in originalTypeParameters.withIndex()) {
|
|
||||||
val (parameterClass, qualifierPartIndex) = typeParametersAlignedToQualifierParts[typeParameter.symbol] ?: continue
|
|
||||||
|
|
||||||
if (typeParameterIndex < typeArgumentsCount) {
|
|
||||||
// Check if type argument matches type parameter in respective qualifier part
|
|
||||||
val qualifierPartArgumentsCount = qualifier[qualifierPartIndex].typeArgumentList.typeArguments.size
|
|
||||||
createDiagnosticsIfExists(
|
|
||||||
parameterClass,
|
|
||||||
qualifierPartIndex,
|
|
||||||
symbol,
|
|
||||||
typeRef,
|
|
||||||
qualifierPartArgumentsCount
|
|
||||||
)?.let { return it }
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeParameter !is FirOuterClassTypeParameterRef ||
|
|
||||||
isValidTypeParameterFromOuterDeclaration(typeParameter.symbol, topContainer, session)
|
|
||||||
) {
|
|
||||||
val type = ConeTypeParameterTypeImpl(ConeTypeParameterLookupTag(typeParameter.symbol), isNullable = false)
|
|
||||||
val substituted = actualSubstitutor.substituteOrNull(type)
|
|
||||||
if (substituted == null) {
|
|
||||||
createDiagnosticsIfExists(
|
|
||||||
parameterClass,
|
|
||||||
qualifierPartIndex,
|
|
||||||
symbol,
|
|
||||||
typeRef,
|
|
||||||
qualifierPartArgumentsCount = null
|
|
||||||
)?.let { return it }
|
|
||||||
} else {
|
|
||||||
allTypeArguments.add(substituted)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return ConeErrorType(ConeOuterClassArgumentsRequired(parameterClass.symbol))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check rest type arguments
|
|
||||||
if (typeArgumentsCount > actualTypeParametersCount) {
|
|
||||||
for (index in qualifier.indices) {
|
|
||||||
if (qualifier[index].typeArgumentList.typeArguments.isNotEmpty()) {
|
|
||||||
val parameterClass = outerDeclarations.elementAtOrNull(index)
|
|
||||||
createDiagnosticsIfExists(
|
|
||||||
parameterClass,
|
|
||||||
index,
|
|
||||||
symbol,
|
|
||||||
typeRef,
|
|
||||||
qualifierPartArgumentsCount = null
|
|
||||||
)?.let { return it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val resultingArguments = allTypeArguments.toTypedArray()
|
val resultingArguments = allTypeArguments.toTypedArray()
|
||||||
@@ -346,103 +268,117 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(SymbolInternals::class)
|
private fun isPossibleBareType(areBareTypesAllowed: Boolean, allTypeArguments: List<ConeTypeProjection>): Boolean =
|
||||||
private fun getClassesAlignedToQualifierParts(
|
areBareTypesAllowed && allTypeArguments.isEmpty()
|
||||||
|
|
||||||
|
private fun matchQualifierPartsAndClasses(
|
||||||
symbol: FirClassLikeSymbol<*>,
|
symbol: FirClassLikeSymbol<*>,
|
||||||
qualifier: List<FirQualifierPart>,
|
qualifier: List<FirQualifierPart>,
|
||||||
session: FirSession
|
typeRef: FirUserTypeRef,
|
||||||
): ParametersMapAndOuterClasses {
|
|
||||||
var currentClassLikeDeclaration: FirClassLikeDeclaration? = null
|
|
||||||
val outerDeclarations = mutableListOf<FirClassLikeDeclaration?>()
|
|
||||||
|
|
||||||
// Try to get at least qualifier.size classes that match qualifier parts
|
|
||||||
var qualifierPartIndex = 0
|
|
||||||
while (qualifierPartIndex < qualifier.size || currentClassLikeDeclaration != null) {
|
|
||||||
if (qualifierPartIndex == 0) {
|
|
||||||
currentClassLikeDeclaration = symbol.fir
|
|
||||||
} else {
|
|
||||||
if (currentClassLikeDeclaration != null) {
|
|
||||||
currentClassLikeDeclaration = currentClassLikeDeclaration.getContainingDeclaration(session)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
outerDeclarations.add(currentClassLikeDeclaration)
|
|
||||||
qualifierPartIndex++
|
|
||||||
}
|
|
||||||
|
|
||||||
val outerArgumentsCount = outerDeclarations.size - qualifier.size
|
|
||||||
val reversedOuterClasses = outerDeclarations.asReversed()
|
|
||||||
val result = mutableMapOf<FirTypeParameterSymbol, ClassWithQualifierPartIndex>()
|
|
||||||
|
|
||||||
for (index in reversedOuterClasses.indices) {
|
|
||||||
currentClassLikeDeclaration = reversedOuterClasses[index]
|
|
||||||
val typeParameters = when (currentClassLikeDeclaration) {
|
|
||||||
is FirTypeAlias -> currentClassLikeDeclaration.typeParameters
|
|
||||||
is FirClass -> currentClassLikeDeclaration.typeParameters
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
if (currentClassLikeDeclaration != null && typeParameters != null) {
|
|
||||||
for (typeParameter in typeParameters) {
|
|
||||||
val typeParameterSymbol = typeParameter.symbol
|
|
||||||
if (!result.containsKey(typeParameterSymbol)) {
|
|
||||||
result[typeParameterSymbol] = ClassWithQualifierPartIndex(currentClassLikeDeclaration, index - outerArgumentsCount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ParametersMapAndOuterClasses(result, reversedOuterClasses.drop(outerArgumentsCount))
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class ParametersMapAndOuterClasses(
|
|
||||||
val parameters: Map<FirTypeParameterSymbol, ClassWithQualifierPartIndex>,
|
|
||||||
val outerClasses: List<FirClassLikeDeclaration?>
|
|
||||||
)
|
|
||||||
|
|
||||||
private data class ClassWithQualifierPartIndex(
|
|
||||||
val klass: FirClassLikeDeclaration,
|
|
||||||
val index: Int
|
|
||||||
)
|
|
||||||
|
|
||||||
@OptIn(SymbolInternals::class)
|
|
||||||
private fun createDiagnosticsIfExists(
|
|
||||||
parameterClass: FirClassLikeDeclaration?,
|
|
||||||
qualifierPartIndex: Int,
|
|
||||||
symbol: FirClassLikeSymbol<*>,
|
|
||||||
userTypeRef: FirUserTypeRef,
|
|
||||||
qualifierPartArgumentsCount: Int?
|
|
||||||
): ConeErrorType? {
|
): ConeErrorType? {
|
||||||
val actualTypeParametersCount = getActualTypeParametersCount(parameterClass ?: symbol.fir)
|
var currentDeclaration: FirClassLikeDeclaration? = symbol.fir
|
||||||
|
var areTypeArgumentsAllowed = true
|
||||||
|
val qualifierPartsCount = qualifier.size
|
||||||
|
|
||||||
if (qualifierPartArgumentsCount == null || actualTypeParametersCount != qualifierPartArgumentsCount) {
|
for ((reversedIndex, qualifierPart) in qualifier.asReversed().withIndex()) {
|
||||||
val source = getTypeArgumentsOrNameSource(userTypeRef, qualifierPartIndex)
|
val qualifierPartIndex = qualifierPartsCount - 1 - reversedIndex
|
||||||
if (source != null) {
|
val qualifierPartArgsCount = qualifierPart.typeArgumentList.typeArguments.size
|
||||||
return ConeErrorType(
|
|
||||||
ConeWrongNumberOfTypeArgumentsError(
|
if (currentDeclaration == null) {
|
||||||
actualTypeParametersCount,
|
// It's a package name
|
||||||
parameterClass?.symbol ?: symbol,
|
// TODO: report TYPE_ARGUMENTS_NOT_ALLOWED instead of WRONG_NUMBER_OF_TYPE_ARGUMENTS
|
||||||
source
|
if (qualifierPartArgsCount > 0) {
|
||||||
|
return createWrongNumberOfTypeArgumentsError(0, qualifierPartIndex, symbol, typeRef)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
val desiredTypeParametersCount = currentDeclaration.typeParameters.count { it !is FirOuterClassTypeParameterRef }
|
||||||
|
if (areTypeArgumentsAllowed) {
|
||||||
|
if (desiredTypeParametersCount != qualifierPartArgsCount) {
|
||||||
|
return createWrongNumberOfTypeArgumentsError(
|
||||||
|
desiredTypeParametersCount,
|
||||||
|
qualifierPartIndex,
|
||||||
|
currentDeclaration.symbol,
|
||||||
|
typeRef
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
} else if (qualifierPartArgsCount > 0) {
|
||||||
|
// TODO: report TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED instead of WRONG_NUMBER_OF_TYPE_ARGUMENTS
|
||||||
|
return createWrongNumberOfTypeArgumentsError(
|
||||||
|
0,
|
||||||
|
qualifierPartIndex,
|
||||||
|
currentDeclaration.symbol,
|
||||||
|
typeRef
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inner class can't contain non-inner class
|
||||||
|
// No more arguments are allowed after first static/non-inner class
|
||||||
|
areTypeArgumentsAllowed = currentDeclaration.isInner
|
||||||
|
currentDeclaration = currentDeclaration.getContainingDeclaration(session)
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getActualTypeParametersCount(element: FirClassLikeDeclaration): Int {
|
private fun createWrongNumberOfTypeArgumentsError(
|
||||||
return (element as FirTypeParameterRefsOwner).typeParameters
|
desiredTypeParametersCount: Int,
|
||||||
.count { it !is FirOuterClassTypeParameterRef }
|
qualifierPartIndex: Int,
|
||||||
}
|
symbol: FirClassLikeSymbol<*>,
|
||||||
|
userTypeRef: FirUserTypeRef,
|
||||||
private fun getTypeArgumentsOrNameSource(typeRef: FirUserTypeRef, qualifierIndex: Int?): KtSourceElement? {
|
): ConeErrorType {
|
||||||
val qualifierPart = if (qualifierIndex != null) typeRef.qualifier.elementAtOrNull(qualifierIndex) else null
|
val qualifierPart = userTypeRef.qualifier[qualifierPartIndex]
|
||||||
val typeArgumentsList = qualifierPart?.typeArgumentList
|
val typeArgumentsList = qualifierPart.typeArgumentList
|
||||||
return if (typeArgumentsList == null || typeArgumentsList.typeArguments.isEmpty()) {
|
val source = if (typeArgumentsList.typeArguments.isEmpty()) {
|
||||||
qualifierPart?.source ?: typeRef.source
|
qualifierPart.source
|
||||||
} else {
|
} else {
|
||||||
typeArgumentsList.source
|
typeArgumentsList.source
|
||||||
}
|
}
|
||||||
|
return ConeErrorType(ConeWrongNumberOfTypeArgumentsError(desiredTypeParametersCount, symbol, source!!))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableList<ConeTypeProjection>.addImplicitTypeArgumentsOrReturnError(
|
||||||
|
symbol: FirClassLikeSymbol<*>,
|
||||||
|
topContainer: FirDeclaration?,
|
||||||
|
substitutor: ConeSubstitutor?,
|
||||||
|
): ConeErrorType? {
|
||||||
|
// substitutor is used for checking if all implicit type arguments are defined in outer classes. Consider the following example:
|
||||||
|
//
|
||||||
|
// class A<T> {
|
||||||
|
// inner class B
|
||||||
|
// val x: B? = null // substitutor returns not null for implicit T, hence there is no error, FQN is not required
|
||||||
|
// class Nested {
|
||||||
|
// val y: B? = null // substitutor returns null for implicit T, hence OUTER_CLASS_ARGUMENTS_REQUIRED is reported here.
|
||||||
|
// // To fix the problem, the FQN should be used, for instance: val y: A<String>.B? = null
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
val explicitTypeArgumentsNumber = size
|
||||||
|
for ((typeParameterIndex, typeParameter) in symbol.fir.typeParameters.withIndex()) {
|
||||||
|
if (typeParameterIndex < explicitTypeArgumentsNumber) {
|
||||||
|
// Ignore explicit type parameters since only outer type parameters are relevant
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeParameter !is FirOuterClassTypeParameterRef
|
||||||
|
|| isValidTypeParameterFromOuterDeclaration(typeParameter.symbol, topContainer, session)
|
||||||
|
) {
|
||||||
|
val substituted = substitutor?.substituteOrNull(typeParameter.symbol.defaultType)
|
||||||
|
if (substituted == null) {
|
||||||
|
return ConeErrorType(
|
||||||
|
ConeOuterClassArgumentsRequired(typeParameter.symbol.containingDeclarationSymbol as FirClassLikeSymbol<*>)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
add(substituted)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ConeErrorType(
|
||||||
|
ConeOuterClassArgumentsRequired(typeParameter.symbol.containingDeclarationSymbol as FirClassLikeSymbol<*>)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createFunctionType(
|
private fun createFunctionType(
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
|
// ISSUE: KT-60921
|
||||||
|
|
||||||
|
abstract class A<T> {
|
||||||
|
inner class B {
|
||||||
|
inner class C
|
||||||
|
|
||||||
|
fun f2(y: Any) {
|
||||||
|
if (y is B<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><*><!>.C) { }
|
||||||
|
|
||||||
|
if (y is B.C<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><*><!>) { }
|
||||||
|
|
||||||
|
if (y is B<*>.C<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><*><!>) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun f1(x: Any) {
|
||||||
|
if (x is B<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><*><!>) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
|
|
||||||
import Outer.Inner
|
|
||||||
|
|
||||||
|
|
||||||
class Outer<E> {
|
|
||||||
inner class Inner
|
|
||||||
|
|
||||||
fun foo() {
|
|
||||||
class E
|
|
||||||
val x: Inner = Inner()
|
|
||||||
}
|
|
||||||
|
|
||||||
class Nested {
|
|
||||||
fun bar(x: <!OUTER_CLASS_ARGUMENTS_REQUIRED!>Inner<!>) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class E
|
|
||||||
|
|
||||||
fun bar(x: <!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Inner<!>) {}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
|
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
|
||||||
import Outer.Inner
|
import Outer.Inner
|
||||||
|
|
||||||
|
|||||||
Generated
+6
@@ -13839,6 +13839,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/bareTypesComplex.kt");
|
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/bareTypesComplex.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("castAnElementToInnerClassWithTypeParameter.kt")
|
||||||
|
public void testCastAnElementToInnerClassWithTypeParameter() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/generics/innerClasses/castAnElementToInnerClassWithTypeParameter.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("checkBoundsOuter.kt")
|
@TestMetadata("checkBoundsOuter.kt")
|
||||||
public void testCheckBoundsOuter() throws Exception {
|
public void testCheckBoundsOuter() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user