[FIR] Add TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED diagnostics

Report TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED instead of
WRONG_NUMBER_OF_TYPE_ARGUMENTS where it's appropriate

^KT-59393 Fixed

Simplify FirTypeResolverImpl
This commit is contained in:
Ivan Kochurkin
2023-09-21 13:13:12 +02:00
committed by Space Team
parent 8d27af5b0d
commit 319fb967ec
18 changed files with 64 additions and 81 deletions
@@ -2123,6 +2123,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED) { firDiagnostic ->
TypeArgumentsForOuterClassWhenNestedReferencedImpl(
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS) { firDiagnostic ->
WrongNumberOfTypeArgumentsImpl(
firDiagnostic.a,
@@ -1513,6 +1513,10 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = TypeArgumentsNotAllowed::class
}
interface TypeArgumentsForOuterClassWhenNestedReferenced : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = TypeArgumentsForOuterClassWhenNestedReferenced::class
}
interface WrongNumberOfTypeArguments : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = WrongNumberOfTypeArguments::class
val expectedCount: Int
@@ -1816,6 +1816,11 @@ internal class TypeArgumentsNotAllowedImpl(
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.TypeArgumentsNotAllowed
internal class TypeArgumentsForOuterClassWhenNestedReferencedImpl(
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.TypeArgumentsForOuterClassWhenNestedReferenced
internal class WrongNumberOfTypeArgumentsImpl(
override val expectedCount: Int,
override val classifier: KtClassLikeSymbol,
@@ -680,6 +680,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<ConeKotlinType>("actualUpperBound")
}
val TYPE_ARGUMENTS_NOT_ALLOWED by error<PsiElement>()
val TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED by error<PsiElement>()
val WRONG_NUMBER_OF_TYPE_ARGUMENTS by error<PsiElement> {
parameter<Int>("expectedCount")
parameter<FirClassLikeSymbol<*>>("classifier")
@@ -421,6 +421,7 @@ object FirErrors {
val UPPER_BOUND_VIOLATED by error2<PsiElement, ConeKotlinType, ConeKotlinType>()
val UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION by error2<PsiElement, ConeKotlinType, ConeKotlinType>()
val TYPE_ARGUMENTS_NOT_ALLOWED by error0<PsiElement>()
val TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED by error0<PsiElement>()
val WRONG_NUMBER_OF_TYPE_ARGUMENTS by error2<PsiElement, Int, FirClassLikeSymbol<*>>()
val NO_TYPE_ARGUMENTS_ON_RHS by error2<PsiElement, Int, FirClassLikeSymbol<*>>()
val OUTER_CLASS_ARGUMENTS_REQUIRED by error1<PsiElement, FirClassLikeSymbol<*>>()
@@ -267,6 +267,7 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set<String> = setOf(
"UPPER_BOUND_VIOLATED",
"UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION",
"TYPE_ARGUMENTS_NOT_ALLOWED",
"TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED",
"WRONG_NUMBER_OF_TYPE_ARGUMENTS",
"NO_TYPE_ARGUMENTS_ON_RHS",
"OUTER_CLASS_ARGUMENTS_REQUIRED",
@@ -596,6 +596,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPEALIAS_EXPANSI
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPEALIAS_SHOULD_EXPAND_TO_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_ARGUMENTS_NOT_ALLOWED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_ARGUMENTS_REDUNDANT_IN_SUPER_QUALIFIER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_ARGUMENT_ON_TYPED_VALUE_CLASS_EQUALS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_CANT_BE_USED_FOR_CONST_VAL
@@ -1273,6 +1274,7 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
)
map.put(TYPE_ARGUMENTS_NOT_ALLOWED, "Type parameters cannot be parameterized with type arguments.") // *
map.put(TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED, "Type arguments for outer class are redundant when nested class is referenced")
val wrongNumberOfTypeArguments = "{0,choice,0#No type arguments|1#One type argument|1<{0,number,integer} type arguments} expected"
map.put(
WRONG_NUMBER_OF_TYPE_ARGUMENTS,
@@ -125,6 +125,8 @@ private fun ConeDiagnostic.toKtDiagnostic(
FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS.createOn(this.source, this.desiredCount, this.symbol)
is ConeTypeArgumentsNotAllowedError ->
FirErrors.TYPE_ARGUMENTS_NOT_ALLOWED.createOn(this.source)
is ConeTypeArgumentsForOuterClassWhenNestedReferencedError ->
FirErrors.TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED.createOn(this.source)
is ConeOuterClassArgumentsRequired ->
FirErrors.OUTER_CLASS_ARGUMENTS_REQUIRED.createOn(callOrAssignmentSource ?: source, this.symbol)
@@ -216,8 +216,9 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
qualifier.reversed().flatMap { it.typeArgumentList.typeArguments }.mapTo(mutableListOf()) { it.toConeTypeProjection() }
if (symbol is FirClassLikeSymbol<*> && !isPossibleBareType(areBareTypesAllowed, allTypeArguments)) {
matchQualifierPartsAndClasses(symbol, qualifier, typeRef)?.let { return it }
allTypeArguments.addImplicitTypeArgumentsOrReturnError(symbol, topContainer, substitutor)?.let { return it }
matchQualifierPartsAndClasses(symbol, qualifier)?.let { return ConeErrorType(it) }
allTypeArguments.addImplicitTypeArgumentsOrReturnError(symbol, topContainer, substitutor)
?.let { return ConeErrorType(it) }
}
val resultingArguments = allTypeArguments.toTypedArray()
@@ -271,23 +272,18 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
private fun isPossibleBareType(areBareTypesAllowed: Boolean, allTypeArguments: List<ConeTypeProjection>): Boolean =
areBareTypesAllowed && allTypeArguments.isEmpty()
private fun matchQualifierPartsAndClasses(
symbol: FirClassLikeSymbol<*>,
qualifier: List<FirQualifierPart>,
typeRef: FirUserTypeRef,
): ConeErrorType? {
private fun matchQualifierPartsAndClasses(symbol: FirClassLikeSymbol<*>, qualifier: List<FirQualifierPart>): ConeDiagnostic? {
var currentDeclaration: FirClassLikeDeclaration? = symbol.fir
var areTypeArgumentsAllowed = true
val qualifierPartsCount = qualifier.size
for ((reversedIndex, qualifierPart) in qualifier.asReversed().withIndex()) {
val qualifierPartIndex = qualifierPartsCount - 1 - reversedIndex
val qualifierPartArgsCount = qualifierPart.typeArgumentList.typeArguments.size
for (qualifierPart in qualifier.asReversed()) {
val typeArgumentList = qualifierPart.typeArgumentList
val qualifierPartArgsCount = typeArgumentList.typeArguments.size
if (currentDeclaration == null) {
// It's a package name
if (qualifierPartArgsCount > 0) {
return ConeErrorType(ConeTypeArgumentsNotAllowedError(qualifierPart.typeArgumentList.source!!))
return ConeTypeArgumentsNotAllowedError(typeArgumentList.source!!)
}
break
}
@@ -295,21 +291,11 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
val desiredTypeParametersCount = currentDeclaration.typeParameters.count { it !is FirOuterClassTypeParameterRef }
if (areTypeArgumentsAllowed) {
if (desiredTypeParametersCount != qualifierPartArgsCount) {
return createWrongNumberOfTypeArgumentsError(
desiredTypeParametersCount,
qualifierPartIndex,
currentDeclaration.symbol,
typeRef
)
val source = if (qualifierPartArgsCount == 0) qualifierPart.source else typeArgumentList.source
return ConeWrongNumberOfTypeArgumentsError(desiredTypeParametersCount, currentDeclaration.symbol, source!!)
}
} 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
)
return ConeTypeArgumentsForOuterClassWhenNestedReferencedError(typeArgumentList.source!!)
}
// Inner class can't contain non-inner class
@@ -321,27 +307,11 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
return null
}
private fun createWrongNumberOfTypeArgumentsError(
desiredTypeParametersCount: Int,
qualifierPartIndex: Int,
symbol: FirClassLikeSymbol<*>,
userTypeRef: FirUserTypeRef,
): ConeErrorType {
val qualifierPart = userTypeRef.qualifier[qualifierPartIndex]
val typeArgumentsList = qualifierPart.typeArgumentList
val source = if (typeArgumentsList.typeArguments.isEmpty()) {
qualifierPart.source
} else {
typeArgumentsList.source
}
return ConeErrorType(ConeWrongNumberOfTypeArgumentsError(desiredTypeParametersCount, symbol, source!!))
}
private fun MutableList<ConeTypeProjection>.addImplicitTypeArgumentsOrReturnError(
symbol: FirClassLikeSymbol<*>,
topContainer: FirDeclaration?,
substitutor: ConeSubstitutor?,
): ConeErrorType? {
): ConeDiagnostic? {
// substitutor is used for checking if all implicit type arguments are defined in outer classes. Consider the following example:
//
// class A<T> {
@@ -364,16 +334,12 @@ class FirTypeResolverImpl(private val session: FirSession) : FirTypeResolver() {
) {
val substituted = substitutor?.substituteOrNull(typeParameter.symbol.defaultType)
if (substituted == null) {
return ConeErrorType(
ConeOuterClassArgumentsRequired(typeParameter.symbol.containingDeclarationSymbol as FirClassLikeSymbol<*>)
)
return ConeOuterClassArgumentsRequired(typeParameter.symbol.containingDeclarationSymbol as FirClassLikeSymbol<*>)
} else {
add(substituted)
}
} else {
return ConeErrorType(
ConeOuterClassArgumentsRequired(typeParameter.symbol.containingDeclarationSymbol as FirClassLikeSymbol<*>)
)
return ConeOuterClassArgumentsRequired(typeParameter.symbol.containingDeclarationSymbol as FirClassLikeSymbol<*>)
}
}
@@ -268,6 +268,10 @@ class ConeTypeArgumentsNotAllowedError(source: KtSourceElement) : ConeDiagnostic
override val reason: String get() = "Type parameters cannot be parameterized with type arguments"
}
class ConeTypeArgumentsForOuterClassWhenNestedReferencedError(source: KtSourceElement) : ConeDiagnosticWithSource(source) {
override val reason: String get() = "Type arguments for outer class are redundant when nested class is referenced"
}
class ConeNoTypeArgumentsOnRhsError(
override val desiredCount: Int,
override val symbol: FirClassLikeSymbol<*>
@@ -23,14 +23,14 @@ fun test() {
<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Foo<String>.Bar.Baz<!>::class
a<Foo<String>.<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Bar<!>>()
a<Foo<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><String><!>.Bar.Baz>()
a<Foo<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><String><!>.Bar.Baz>()
a<Foo.Bar<Int>>()
a<Foo.Bar<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><Int><!>.Baz>()
a<Foo.Bar<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><Int><!>.Baz>()
}
fun <T: Foo<String.<!UNRESOLVED_REFERENCE!>Bar<!>>> x() {}
fun Foo<String>.<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Bar<!>.ext() {}
fun ex1(a: Foo<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><String><!>.Bar<String>): Foo<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><String><!>.Bar<String> {
fun ex1(a: Foo<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><String><!>.Bar<String>): Foo<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><String><!>.Bar<String> {
<!NO_RETURN_IN_FUNCTION_WITH_BLOCK_BODY!>}<!>
@@ -44,8 +44,8 @@ fun error1(): Outer<A>.Inner<B>.Inner3<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><C, D><!
fun error2(): Outer<A>.Inner<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><B, C, D><!>.Inner2 = null!!
fun error3(): <!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Outer<!>.Inner<A, B>.Inner3<C> = null!!
fun error4(): Outer<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><A><!>.Nested<B>.Inner4<C> = null!!
fun error5(): Outer<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><A><!>.Obj.Nested2<B>.Inner5<C> = null!!
fun error6(): Outer.Obj<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><A><!>.Nested2<B>.Inner5<C> = null!!
fun error4(): Outer<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><A><!>.Nested<B>.Inner4<C> = null!!
fun error5(): Outer<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><A><!>.Obj.Nested2<B>.Inner5<C> = null!!
fun error6(): Outer.Obj<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><A><!>.Nested2<B>.Inner5<C> = null!!
fun error7(): test<!TYPE_ARGUMENTS_NOT_ALLOWED!><String><!>.Outer.Obj.Nested2<A>.Inner5<B> = null!!
@@ -16,5 +16,5 @@ class Derived : Base<Int>() {
val x2: Base<Int>.InnerCell = InnerCell(42)
val test1: <!UNRESOLVED_REFERENCE!>CT<!> = Cell(42)
val test2: Base<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><Int><!>.CT = Cell(42)
val test2: Base<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><Int><!>.CT = Cell(42)
}
@@ -18,17 +18,17 @@ class Outer<T> {
fun test5(x: GenericInnerAlias<Int>) = x
fun <T> test6(x: GenericInnerAlias<T>) = x
}
fun test1(x: Outer<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><Int><!>.NestedAlias) = x
fun <T> test2(x: Outer<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><T><!>.NestedAlias) = x
fun test1(x: Outer<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><Int><!>.NestedAlias) = x
fun <T> test2(x: Outer<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><T><!>.NestedAlias) = x
fun test3(x: Outer.NestedAlias) = x
fun test4(x: Outer<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><Int><!>.GenericNestedAlias<Int>) = x
fun <T> test5(x: Outer<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><T><!>.GenericNestedAlias<Int>) = x
fun <T> test6(x: Outer<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><Int><!>.GenericNestedAlias<T>) = x
fun test4(x: Outer<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><Int><!>.GenericNestedAlias<Int>) = x
fun <T> test5(x: Outer<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><T><!>.GenericNestedAlias<Int>) = x
fun <T> test6(x: Outer<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><Int><!>.GenericNestedAlias<T>) = x
fun test7(x: Outer.GenericNestedAlias<Int>) = x
fun <T> test8(x: Outer.GenericNestedAlias<T>) = x
fun test9(x: Outer.InnerAlias) = x
fun test10(x: Outer<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><Int><!>.InnerAlias) = x
fun <T> test11(x: Outer<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><T><!>.InnerAlias) = x
fun test10(x: Outer<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><Int><!>.InnerAlias) = x
fun <T> test11(x: Outer<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><T><!>.InnerAlias) = x
fun test12(x: Outer.GenericInnerAlias<Int>) = x
fun test13(x: Outer<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><Int><!>.GenericInnerAlias<Int>) = x
fun <T> test14(x: Outer<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><T><!>.GenericInnerAlias<Int>) = x
fun test13(x: Outer<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><Int><!>.GenericInnerAlias<Int>) = x
fun <T> test14(x: Outer<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><T><!>.GenericInnerAlias<Int>) = x
@@ -11,14 +11,14 @@ class C<T> {
typealias SSC = C<*>
}
fun test1(x: C<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><Int><!>.DA) = x
fun test1(x: C<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><Int><!>.DA) = x
fun test2(x: C.SDA) = x
fun test3(x: C<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><Int><!>.TSDA) = x
fun test4(x: C<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><Int><!>.TC) = x
fun test3(x: C<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><Int><!>.TSDA) = x
fun test4(x: C<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><Int><!>.TC) = x
fun test5(x: C<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><*><!>.DA) = x
fun test6(x: C<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><*><!>.TSDA) = x
fun test7(x: C<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><*><!>.TC) = x
fun test5(x: C<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><*><!>.DA) = x
fun test6(x: C<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><*><!>.TSDA) = x
fun test7(x: C<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><*><!>.TC) = x
fun test8(x: C.SSDA) = x
fun test9(x: C.SSC) = x
@@ -1,10 +0,0 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER -TOPLEVEL_TYPEALIASES_ONLY
class OuterClass<T1> {
class NestedClass<T2>
typealias NestedType<T> = NestedClass<T>
}
typealias ON1<T1, T2> = OuterClass<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><T1><!>.NestedClass<T2>
typealias ON2<T1, T2> = OuterClass<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><T1><!>.NestedType<T2>
typealias ON3<T2> = OuterClass.NestedType<T2>
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER -TOPLEVEL_TYPEALIASES_ONLY
class OuterClass<T1> {
@@ -6,4 +6,4 @@ class C<T> {
typealias P2 = Pair<T, Int>
}
val p1: C<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!><String><!>.P2 = Pair("", 1)
val p1: C<!TYPE_ARGUMENTS_FOR_OUTER_CLASS_WHEN_NESTED_REFERENCED!><String><!>.P2 = Pair("", 1)