[FIR] Implement UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION diagnostics, fix handling of UPPER_BOUND_VIOLATED
This commit is contained in:
committed by
teamcityserver
parent
5741374883
commit
66e2b44272
+1
-1
@@ -45,7 +45,7 @@ fun <K, L : K> rest() {
|
|||||||
class NumColl<T : Collection<Number>>
|
class NumColl<T : Collection<Number>>
|
||||||
typealias NL<K> = NumColl<List<K>>
|
typealias NL<K> = NumColl<List<K>>
|
||||||
val test7 = NL<Int>()<!UNRESOLVED_REFERENCE!>NumberPhile<!><!SYNTAX!><!>
|
val test7 = NL<Int>()<!UNRESOLVED_REFERENCE!>NumberPhile<!><!SYNTAX!><!>
|
||||||
val test8 = NL<String>()
|
val test8 = NL<<!UPPER_BOUND_VIOLATED!>String<!>>()
|
||||||
|
|
||||||
class NumberPhile<T: Number>(x: T)
|
class NumberPhile<T: Number>(x: T)
|
||||||
val np1 = NumberPhile(10)
|
val np1 = NumberPhile(10)
|
||||||
|
|||||||
+2
-2
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import java.util.EnumMap
|
import java.util.EnumMap
|
||||||
|
|
||||||
typealias SomeEnumMap = EnumMap<String, String?>
|
typealias SomeEnumMap = EnumMap<<!UPPER_BOUND_VIOLATED!>String<!>, String?>
|
||||||
|
|
||||||
fun test(map: SomeEnumMap, qualifier: String?) {
|
fun test(map: SomeEnumMap, qualifier: String?) {
|
||||||
val result = map[qualifier]
|
val result = map[qualifier]
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -402,7 +402,12 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
|||||||
val INFERENCE_ERROR by error<PsiElement>()
|
val INFERENCE_ERROR by error<PsiElement>()
|
||||||
val PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT by error<PsiElement>()
|
val PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT by error<PsiElement>()
|
||||||
val UPPER_BOUND_VIOLATED by error<PsiElement> {
|
val UPPER_BOUND_VIOLATED by error<PsiElement> {
|
||||||
parameter<ConeKotlinType>("upperBound")
|
parameter<ConeKotlinType>("expectedUpperBound")
|
||||||
|
parameter<ConeKotlinType>("actualUpperBound")
|
||||||
|
}
|
||||||
|
val UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION by error<PsiElement> {
|
||||||
|
parameter<ConeKotlinType>("expectedUpperBound")
|
||||||
|
parameter<ConeKotlinType>("actualUpperBound")
|
||||||
}
|
}
|
||||||
val TYPE_ARGUMENTS_NOT_ALLOWED by error<PsiElement>()
|
val TYPE_ARGUMENTS_NOT_ALLOWED by error<PsiElement>()
|
||||||
val WRONG_NUMBER_OF_TYPE_ARGUMENTS by error<PsiElement> {
|
val WRONG_NUMBER_OF_TYPE_ARGUMENTS by error<PsiElement> {
|
||||||
|
|||||||
+2
-1
@@ -282,7 +282,8 @@ object FirErrors {
|
|||||||
val RECURSION_IN_IMPLICIT_TYPES by error0<PsiElement>()
|
val RECURSION_IN_IMPLICIT_TYPES by error0<PsiElement>()
|
||||||
val INFERENCE_ERROR by error0<PsiElement>()
|
val INFERENCE_ERROR by error0<PsiElement>()
|
||||||
val PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT by error0<PsiElement>()
|
val PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT by error0<PsiElement>()
|
||||||
val UPPER_BOUND_VIOLATED by error1<PsiElement, ConeKotlinType>()
|
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_NOT_ALLOWED by error0<PsiElement>()
|
||||||
val WRONG_NUMBER_OF_TYPE_ARGUMENTS by error2<PsiElement, Int, FirClassLikeSymbol<*>>()
|
val WRONG_NUMBER_OF_TYPE_ARGUMENTS by error2<PsiElement, Int, FirClassLikeSymbol<*>>()
|
||||||
val NO_TYPE_ARGUMENTS_ON_RHS by error2<PsiElement, Int, FirClassLikeSymbol<*>>()
|
val NO_TYPE_ARGUMENTS_ON_RHS by error2<PsiElement, Int, FirClassLikeSymbol<*>>()
|
||||||
|
|||||||
@@ -581,7 +581,7 @@ internal fun checkCondition(condition: FirExpression, context: CheckerContext, r
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun extractTypeRefAndSourceFromTypeArgument(typeRef: FirTypeRef?, index: Int): Pair<FirTypeRef, FirSourceElement?>? {
|
fun extractArgumentTypeRefAndSource(typeRef: FirTypeRef?, index: Int): FirTypeRefSource? {
|
||||||
if (typeRef is FirResolvedTypeRef) {
|
if (typeRef is FirResolvedTypeRef) {
|
||||||
val delegatedTypeRef = typeRef.delegatedTypeRef
|
val delegatedTypeRef = typeRef.delegatedTypeRef
|
||||||
if (delegatedTypeRef is FirUserTypeRef) {
|
if (delegatedTypeRef is FirUserTypeRef) {
|
||||||
@@ -601,20 +601,22 @@ fun extractTypeRefAndSourceFromTypeArgument(typeRef: FirTypeRef?, index: Int): P
|
|||||||
|
|
||||||
val typeArgument = currentTypeArguments?.elementAtOrNull(currentIndex)
|
val typeArgument = currentTypeArguments?.elementAtOrNull(currentIndex)
|
||||||
if (typeArgument is FirTypeProjectionWithVariance) {
|
if (typeArgument is FirTypeProjectionWithVariance) {
|
||||||
return Pair(typeArgument.typeRef, typeArgument.source)
|
return FirTypeRefSource(typeArgument.typeRef, typeArgument.source)
|
||||||
}
|
}
|
||||||
} else if (delegatedTypeRef is FirFunctionTypeRef) {
|
} else if (delegatedTypeRef is FirFunctionTypeRef) {
|
||||||
val valueParameters = delegatedTypeRef.valueParameters
|
val valueParameters = delegatedTypeRef.valueParameters
|
||||||
if (index < valueParameters.size) {
|
if (index < valueParameters.size) {
|
||||||
val valueParamTypeRef = valueParameters.elementAt(index).returnTypeRef
|
val valueParamTypeRef = valueParameters.elementAt(index).returnTypeRef
|
||||||
return Pair(valueParamTypeRef, valueParamTypeRef.source)
|
return FirTypeRefSource(valueParamTypeRef, valueParamTypeRef.source)
|
||||||
}
|
}
|
||||||
if (index == valueParameters.size) {
|
if (index == valueParameters.size) {
|
||||||
val returnTypeRef = delegatedTypeRef.returnTypeRef
|
val returnTypeRef = delegatedTypeRef.returnTypeRef
|
||||||
return Pair(returnTypeRef, returnTypeRef.source)
|
return FirTypeRefSource(returnTypeRef, returnTypeRef.source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class FirTypeRefSource(val typeRef: FirTypeRef?, val source: FirSourceElement?)
|
||||||
+4
-4
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
|||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.fir.*
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.extractTypeRefAndSourceFromTypeArgument
|
import org.jetbrains.kotlin.fir.analysis.checkers.extractArgumentTypeRefAndSource
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||||
@@ -155,11 +155,11 @@ object FirClassVarianceChecker : FirClassChecker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (newVariance != null) {
|
if (newVariance != null) {
|
||||||
val subTypeRefAndSource = extractTypeRefAndSourceFromTypeArgument(typeRef, index)
|
val subTypeRefAndSource = extractArgumentTypeRefAndSource(typeRef, index)
|
||||||
|
|
||||||
checkVarianceConflict(
|
checkVarianceConflict(
|
||||||
typeArgumentType, newVariance, subTypeRefAndSource?.first, containingType,
|
typeArgumentType, newVariance, subTypeRefAndSource?.typeRef, containingType,
|
||||||
context, reporter, subTypeRefAndSource?.first?.source ?: source,
|
context, reporter, subTypeRefAndSource?.typeRef?.source ?: source,
|
||||||
fullyExpandedType != type
|
fullyExpandedType != type
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.extractTypeRefAndSourceFromTypeArgument
|
import org.jetbrains.kotlin.fir.analysis.checkers.extractArgumentTypeRefAndSource
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||||
@@ -77,11 +77,11 @@ object FirProjectionRelationChecker : FirBasicDeclarationChecker() {
|
|||||||
ProjectionRelation.None
|
ProjectionRelation.None
|
||||||
}
|
}
|
||||||
|
|
||||||
val (typeArgTypeRef, typeArgSource) = extractTypeRefAndSourceFromTypeArgument(typeRef, it) ?: continue
|
val argTypeRefSource = extractArgumentTypeRefAndSource(typeRef, it) ?: continue
|
||||||
|
|
||||||
if (projectionRelation != ProjectionRelation.None && typeRef.source?.kind !is FirFakeSourceElementKind) {
|
if (projectionRelation != ProjectionRelation.None && typeRef.source?.kind !is FirFakeSourceElementKind) {
|
||||||
reporter.reportOn(
|
reporter.reportOn(
|
||||||
typeArgSource ?: typeArgTypeRef.source,
|
argTypeRefSource.source ?: argTypeRefSource.typeRef?.source,
|
||||||
if (projectionRelation == ProjectionRelation.Conflicting)
|
if (projectionRelation == ProjectionRelation.Conflicting)
|
||||||
if (type != fullyExpandedType) FirErrors.CONFLICTING_PROJECTION_IN_TYPEALIAS_EXPANSION else FirErrors.CONFLICTING_PROJECTION
|
if (type != fullyExpandedType) FirErrors.CONFLICTING_PROJECTION_IN_TYPEALIAS_EXPANSION else FirErrors.CONFLICTING_PROJECTION
|
||||||
else
|
else
|
||||||
@@ -91,7 +91,7 @@ object FirProjectionRelationChecker : FirBasicDeclarationChecker() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkTypeRef(typeArgTypeRef, context, reporter)
|
argTypeRefSource.typeRef?.let { checkTypeRef(it, context, reporter) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+78
-33
@@ -5,10 +5,12 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.FirTypeRefSource
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.extractTypeRefAndSourceFromTypeArgument
|
import org.jetbrains.kotlin.fir.analysis.checkers.extractArgumentTypeRefAndSource
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||||
@@ -17,7 +19,7 @@ import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
|||||||
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
||||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError
|
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError
|
||||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||||
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
|
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
|
||||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||||
@@ -33,16 +35,20 @@ object FirUpperBoundViolatedClassChecker : FirBasicDeclarationChecker() {
|
|||||||
for (typeParameter in declaration.typeParameters) {
|
for (typeParameter in declaration.typeParameters) {
|
||||||
if (typeParameter is FirTypeParameter) {
|
if (typeParameter is FirTypeParameter) {
|
||||||
for (bound in typeParameter.bounds) {
|
for (bound in typeParameter.bounds) {
|
||||||
analyzeTypeParameters(bound, context, reporter)
|
checkUpperBoundViolated(bound, context, reporter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (superTypeRef in declaration.superTypeRefs) {
|
for (superTypeRef in declaration.superTypeRefs) {
|
||||||
analyzeTypeParameters(superTypeRef, context, reporter)
|
checkUpperBoundViolated(superTypeRef, context, reporter)
|
||||||
}
|
}
|
||||||
|
} else if (declaration is FirTypeAlias) {
|
||||||
|
checkUpperBoundViolated(declaration.expandedTypeRef, context, reporter, isIgnoreTypeParameters = true)
|
||||||
} else if (declaration is FirCallableDeclaration<*>) {
|
} else if (declaration is FirCallableDeclaration<*>) {
|
||||||
analyzeTypeParameters(declaration.returnTypeRef, context, reporter)
|
if (declaration.returnTypeRef.source?.kind !is FirFakeSourceElementKind) {
|
||||||
|
checkUpperBoundViolated(declaration.returnTypeRef, context, reporter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,12 +73,25 @@ object FirUpperBoundViolatedExpressionChecker : FirQualifiedAccessExpressionChec
|
|||||||
calleeFir = calleReference.candidateSymbol?.fir.safeAs()
|
calleeFir = calleReference.candidateSymbol?.fir.safeAs()
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzeTypeParameters(
|
var typeArguments: List<Any>? = null
|
||||||
|
var typeArgumentRefsAndSources: List<FirTypeRefSource?>? = null
|
||||||
|
val typeParameters = if (calleeFir is FirConstructor) {
|
||||||
|
typeArgumentRefsAndSources =
|
||||||
|
expression.typeArguments.map { FirTypeRefSource((it as? FirTypeProjectionWithVariance)?.typeRef, it.source) }
|
||||||
|
val prototypeClass = calleeFir.dispatchReceiverType?.toSymbol(context.session)?.fir.safeAs<FirRegularClass>()
|
||||||
|
prototypeClass?.typeParameters?.map { it.symbol }
|
||||||
|
} else {
|
||||||
|
typeArguments = expression.typeArguments
|
||||||
|
calleeFir?.typeParameters?.map { it.symbol }
|
||||||
|
}
|
||||||
|
|
||||||
|
checkUpperBoundViolated(
|
||||||
expression.typeRef,
|
expression.typeRef,
|
||||||
context,
|
context,
|
||||||
reporter,
|
reporter,
|
||||||
calleeFir?.typeParameters?.map { it.symbol },
|
typeParameters,
|
||||||
expression.typeArguments
|
typeArguments,
|
||||||
|
typeArgumentRefsAndSources
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,12 +100,15 @@ object FirUpperBoundViolatedExpressionChecker : FirQualifiedAccessExpressionChec
|
|||||||
* Recursively analyzes type parameters and reports the diagnostic on the given source calculated using typeRef
|
* Recursively analyzes type parameters and reports the diagnostic on the given source calculated using typeRef
|
||||||
* Returns true if an error occurred
|
* Returns true if an error occurred
|
||||||
*/
|
*/
|
||||||
private fun analyzeTypeParameters(
|
private fun checkUpperBoundViolated(
|
||||||
typeRef: FirTypeRef?,
|
typeRef: FirTypeRef?,
|
||||||
context: CheckerContext,
|
context: CheckerContext,
|
||||||
reporter: DiagnosticReporter,
|
reporter: DiagnosticReporter,
|
||||||
typeParameters: List<FirTypeParameterSymbol>? = null,
|
typeParameters: List<FirTypeParameterSymbol>? = null,
|
||||||
typeArguments: List<FirTypeProjection>? = null
|
typeArguments: List<Any>? = null,
|
||||||
|
typeArgumentRefsAndSources: List<FirTypeRefSource?>? = null,
|
||||||
|
isTypeAlias: Boolean = false,
|
||||||
|
isIgnoreTypeParameters: Boolean = false
|
||||||
) {
|
) {
|
||||||
val type = when (typeRef) {
|
val type = when (typeRef) {
|
||||||
is ConeKotlinType -> typeRef
|
is ConeKotlinType -> typeRef
|
||||||
@@ -101,10 +123,26 @@ private fun analyzeTypeParameters(
|
|||||||
|
|
||||||
val typeParameterSymbols = typeParameters
|
val typeParameterSymbols = typeParameters
|
||||||
?: if (type is ConeClassLikeType) {
|
?: if (type is ConeClassLikeType) {
|
||||||
val prototypeClass = type.lookupTag.toSymbol(context.session)
|
val fullyExpandedType = type.fullyExpandedType(context.session)
|
||||||
|
val prototypeClass = fullyExpandedType.lookupTag.toSymbol(context.session)
|
||||||
?.fir.safeAs<FirRegularClass>()
|
?.fir.safeAs<FirRegularClass>()
|
||||||
?: return
|
?: return
|
||||||
|
|
||||||
|
if (type != fullyExpandedType) {
|
||||||
|
// special check for type aliases
|
||||||
|
checkUpperBoundViolated(
|
||||||
|
typeRef,
|
||||||
|
context,
|
||||||
|
reporter,
|
||||||
|
prototypeClass.typeParameters.map { it.symbol },
|
||||||
|
fullyExpandedType.typeArguments.toList(),
|
||||||
|
null,
|
||||||
|
isTypeAlias = true,
|
||||||
|
isIgnoreTypeParameters = isIgnoreTypeParameters
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
prototypeClass.typeParameters.map { it.symbol }
|
prototypeClass.typeParameters.map { it.symbol }
|
||||||
} else {
|
} else {
|
||||||
listOf()
|
listOf()
|
||||||
@@ -142,36 +180,43 @@ private fun analyzeTypeParameters(
|
|||||||
typeArgumentTypeRef = localTypeArgument.typeRef
|
typeArgumentTypeRef = localTypeArgument.typeRef
|
||||||
typeArgument = typeArgumentTypeRef.coneType
|
typeArgument = typeArgumentTypeRef.coneType
|
||||||
typeArgumentSource = localTypeArgument.source
|
typeArgumentSource = localTypeArgument.source
|
||||||
|
} else if (localTypeArgument is ConeKotlinType) {
|
||||||
|
// Typealias case
|
||||||
|
typeArgument = localTypeArgument
|
||||||
|
typeArgumentSource = typeRef.source
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
typeArgument = type.typeArguments[index] as? ConeKotlinType
|
typeArgument = type.typeArguments[index] as? ConeKotlinType
|
||||||
val argTypeRefSource = extractTypeRefAndSourceFromTypeArgument(typeRef, index)
|
val typeArgumentRefAndSource = typeArgumentRefsAndSources?.elementAtOrNull(index)
|
||||||
if (argTypeRefSource != null) {
|
if (typeArgumentRefAndSource != null) {
|
||||||
typeArgumentTypeRef = argTypeRefSource.first
|
typeArgumentTypeRef = typeArgumentRefAndSource.typeRef
|
||||||
typeArgumentSource = argTypeRefSource.second
|
typeArgumentSource = typeArgumentRefAndSource.source
|
||||||
|
} else {
|
||||||
|
val extractedTypeArgumentRefAndSource = extractArgumentTypeRefAndSource(typeRef, index)
|
||||||
|
if (extractedTypeArgumentRefAndSource != null) {
|
||||||
|
typeArgumentTypeRef = extractedTypeArgumentRefAndSource.typeRef
|
||||||
|
typeArgumentSource = extractedTypeArgumentRefAndSource.source
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeArgument != null && typeArgumentSource != null) {
|
if (typeArgument != null && typeArgumentSource != null) {
|
||||||
val upperBound = getSubstitutedUpperBound(typeParameterSymbols[index], substitutor, typeSystemContext)
|
if (!isIgnoreTypeParameters || (typeArgument.typeArguments.isEmpty() && typeArgument !is ConeTypeParameterType)) {
|
||||||
if (upperBound != null && !satisfiesBounds(upperBound, typeArgument.type, typeSystemContext)) {
|
val intersection = typeSystemContext.intersectTypes(typeParameterSymbols[index].fir.bounds.map { it.coneType }) as? ConeKotlinType
|
||||||
reporter.reportOn(typeArgumentSource, FirErrors.UPPER_BOUND_VIOLATED, upperBound, context)
|
if (intersection != null) {
|
||||||
|
val upperBound = substitutor.substituteOrSelf(intersection)
|
||||||
|
if (!AbstractTypeChecker.isSubtypeOf(typeSystemContext, typeArgument.type, upperBound, stubTypesEqualToAnything = false)) {
|
||||||
|
val factory =
|
||||||
|
if (isTypeAlias) FirErrors.UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION else FirErrors.UPPER_BOUND_VIOLATED
|
||||||
|
reporter.reportOn(typeArgumentSource, factory, upperBound, typeArgument.type, context)
|
||||||
|
if (isTypeAlias) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
analyzeTypeParameters(typeArgumentTypeRef, context, reporter)
|
checkUpperBoundViolated(typeArgumentTypeRef, context, reporter, isIgnoreTypeParameters = isIgnoreTypeParameters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSubstitutedUpperBound(
|
|
||||||
prototypeSymbol: FirTypeParameterSymbol,
|
|
||||||
substitutor: ConeSubstitutor,
|
|
||||||
typeSystemContext: ConeTypeContext
|
|
||||||
): ConeKotlinType? {
|
|
||||||
val intersection = typeSystemContext.intersectTypes(prototypeSymbol.fir.bounds.map { it.coneType }) as? ConeKotlinType ?: return null
|
|
||||||
return substitutor.substituteOrSelf(intersection)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun satisfiesBounds(upperBound: ConeKotlinType, target: ConeKotlinType, typeSystemContext: ConeTypeContext): Boolean {
|
|
||||||
return AbstractTypeChecker.isSubtypeOf(typeSystemContext, target, upperBound, stubTypesEqualToAnything = false)
|
|
||||||
}
|
|
||||||
+9
-1
@@ -340,6 +340,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSUPPORTED_FEATU
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNUSED_VARIABLE
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNUSED_VARIABLE
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_VIOLATED
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_VIOLATED
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USAGE_IS_NOT_INLINABLE
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USAGE_IS_NOT_INLINABLE
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_CAST
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_CAST
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_ELVIS
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_ELVIS
|
||||||
@@ -649,7 +650,14 @@ class FirDefaultErrorMessages {
|
|||||||
map.put(RECURSION_IN_IMPLICIT_TYPES, "Recursion in implicit types")
|
map.put(RECURSION_IN_IMPLICIT_TYPES, "Recursion in implicit types")
|
||||||
map.put(INFERENCE_ERROR, "Inference error")
|
map.put(INFERENCE_ERROR, "Inference error")
|
||||||
map.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections are not allowed on type arguments of functions and properties")
|
map.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections are not allowed on type arguments of functions and properties")
|
||||||
map.put(UPPER_BOUND_VIOLATED, "Type argument is not within its bounds: should be subtype of ''{0}''", RENDER_TYPE)
|
map.put(UPPER_BOUND_VIOLATED, "Type argument is not within its bounds: should be subtype of ''{0}''", RENDER_TYPE, RENDER_TYPE)
|
||||||
|
map.put(
|
||||||
|
UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION,
|
||||||
|
"Type argument is not within its bounds: should be subtype of ''{0}''",
|
||||||
|
RENDER_TYPE,
|
||||||
|
RENDER_TYPE
|
||||||
|
)
|
||||||
|
|
||||||
map.put(TYPE_ARGUMENTS_NOT_ALLOWED, "Type arguments are not allowed for type parameters") // *
|
map.put(TYPE_ARGUMENTS_NOT_ALLOWED, "Type arguments are not allowed for type parameters") // *
|
||||||
map.put(
|
map.put(
|
||||||
WRONG_NUMBER_OF_TYPE_ARGUMENTS,
|
WRONG_NUMBER_OF_TYPE_ARGUMENTS,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ abstract class AbstractConeSubstitutor(private val typeContext: ConeTypeContext)
|
|||||||
is ConeStarProjection -> old
|
is ConeStarProjection -> old
|
||||||
is ConeKotlinTypeProjectionIn -> ConeKotlinTypeProjectionIn(newType)
|
is ConeKotlinTypeProjectionIn -> ConeKotlinTypeProjectionIn(newType)
|
||||||
is ConeKotlinTypeProjectionOut -> ConeKotlinTypeProjectionOut(newType)
|
is ConeKotlinTypeProjectionOut -> ConeKotlinTypeProjectionOut(newType)
|
||||||
|
is ConeKotlinTypeConflictingProjection -> ConeKotlinTypeConflictingProjection(newType)
|
||||||
is ConeKotlinType -> newType
|
is ConeKotlinType -> newType
|
||||||
else -> old
|
else -> old
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -4,8 +4,8 @@ typealias N<T> = Num<T>
|
|||||||
typealias N2<T> = N<T>
|
typealias N2<T> = N<T>
|
||||||
|
|
||||||
val x1 = Num<<!UPPER_BOUND_VIOLATED!>String<!>>(<!ARGUMENT_TYPE_MISMATCH!>""<!>)
|
val x1 = Num<<!UPPER_BOUND_VIOLATED!>String<!>>(<!ARGUMENT_TYPE_MISMATCH!>""<!>)
|
||||||
val x2 = N<String>("")
|
val x2 = N<<!UPPER_BOUND_VIOLATED!>String<!>>("")
|
||||||
val x3 = N2<String>("")
|
val x3 = N2<<!UPPER_BOUND_VIOLATED!>String<!>>("")
|
||||||
|
|
||||||
class TColl<T, C : Collection<T>>
|
class TColl<T, C : Collection<T>>
|
||||||
|
|
||||||
@@ -13,5 +13,5 @@ typealias TC<T, C> = TColl<T, C>
|
|||||||
typealias TC2<T, C> = TC<T, C>
|
typealias TC2<T, C> = TC<T, C>
|
||||||
|
|
||||||
val y1 = TColl<Any, <!UPPER_BOUND_VIOLATED!>Any<!>>()
|
val y1 = TColl<Any, <!UPPER_BOUND_VIOLATED!>Any<!>>()
|
||||||
val y2 = TC<Any, Any>()
|
val y2 = TC<Any, <!UPPER_BOUND_VIOLATED!>Any<!>>()
|
||||||
val y3 = TC2<Any, Any>()
|
val y3 = TC2<Any, <!UPPER_BOUND_VIOLATED!>Any<!>>()
|
||||||
|
|||||||
Vendored
-16
@@ -1,16 +0,0 @@
|
|||||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
|
|
||||||
|
|
||||||
class TColl<T, C : Collection<T>>
|
|
||||||
|
|
||||||
typealias TC<T1, T2> = TColl<T1, T2>
|
|
||||||
typealias TC2<T1, T2> = TC<T1, T2>
|
|
||||||
|
|
||||||
fun test1(x: TC2<Number, Collection<Number>>) {}
|
|
||||||
fun test2(x: TC2<Number, Collection<Int>>) {}
|
|
||||||
fun test3(x: TC2<Number, List<Int>>) {}
|
|
||||||
fun test4(x: TC2<Number, List<Any>>) {}
|
|
||||||
|
|
||||||
val test5 = TC2<Number, Collection<Number>>()
|
|
||||||
val test6 = TC2<Number, Collection<Int>>()
|
|
||||||
val test7 = TC2<Number, List<Int>>()
|
|
||||||
val test8 = TC2<Number, List<Any>>()
|
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
|
// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER
|
||||||
|
|
||||||
class TColl<T, C : Collection<T>>
|
class TColl<T, C : Collection<T>>
|
||||||
|
|||||||
+6
-6
@@ -10,22 +10,22 @@ typealias MMMM<A3> = NL<A3>
|
|||||||
typealias TC<T1, T2> = TColl<T1, T2>
|
typealias TC<T1, T2> = TColl<T1, T2>
|
||||||
|
|
||||||
fun test1(x: NA<Int>) {}
|
fun test1(x: NA<Int>) {}
|
||||||
fun test2(x: NA<Any>) {}
|
fun test2(x: <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION!>NA<Any><!>) {}
|
||||||
fun test3(x: NL<Int>) {}
|
fun test3(x: NL<Int>) {}
|
||||||
fun test4(x: NL<Any>) {}
|
fun test4(x: <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION!>NL<Any><!>) {}
|
||||||
|
|
||||||
val test5 = NA<Int>()
|
val test5 = NA<Int>()
|
||||||
val test6 = NA<Any>()
|
val test6 = NA<<!UPPER_BOUND_VIOLATED!>Any<!>>()
|
||||||
val test7 = NL<Int>()
|
val test7 = NL<Int>()
|
||||||
val test8 = MMMM<Int>()
|
val test8 = MMMM<Int>()
|
||||||
val test9dwd = NL<Any>()
|
val test9dwd = NL<<!UPPER_BOUND_VIOLATED!>Any<!>>()
|
||||||
|
|
||||||
fun test9(x: TC<Number, Collection<Number>>) {}
|
fun test9(x: TC<Number, Collection<Number>>) {}
|
||||||
fun test10(x: TC<Number, Collection<Int>>) {}
|
fun test10(x: TC<Number, Collection<Int>>) {}
|
||||||
fun test11(x: TC<Number, List<Int>>) {}
|
fun test11(x: TC<Number, List<Int>>) {}
|
||||||
fun test12(x: TC<Number, List<Any>>) {}
|
fun test12(x: <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION!>TC<Number, List<Any>><!>) {}
|
||||||
|
|
||||||
val test13 = TC<Number, Collection<Number>>()
|
val test13 = TC<Number, Collection<Number>>()
|
||||||
val test14 = TC<Number, Collection<Int>>()
|
val test14 = TC<Number, Collection<Int>>()
|
||||||
val test15 = TC<Number, List<Int>>()
|
val test15 = TC<Number, List<Int>>()
|
||||||
val test16 = TC<Number, List<Any>>()
|
val test16 = TC<Number, <!UPPER_BOUND_VIOLATED!>List<Any><!>>()
|
||||||
|
|||||||
+8
-8
@@ -3,18 +3,18 @@
|
|||||||
class TC<T, C : Collection<T>>
|
class TC<T, C : Collection<T>>
|
||||||
|
|
||||||
typealias TCAlias<T, C> = TC<T, C>
|
typealias TCAlias<T, C> = TC<T, C>
|
||||||
typealias TCAliasT<T> = TC<T, Any>
|
typealias TCAliasT<T> = TC<T, <!UPPER_BOUND_VIOLATED!>Any<!>>
|
||||||
typealias TCAliasC<C> = TC<Any, C>
|
typealias TCAliasC<C> = TC<Any, C>
|
||||||
typealias TCAliasT1<T> = TCAlias<T, Any>
|
typealias TCAliasT1<T> = <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION!>TCAlias<T, Any><!>
|
||||||
typealias TCAliasC1<C> = TCAlias<Any, C>
|
typealias TCAliasC1<C> = TCAlias<Any, C>
|
||||||
|
|
||||||
typealias Test1 = TC<Any, Any>
|
typealias Test1 = TC<Any, <!UPPER_BOUND_VIOLATED!>Any<!>>
|
||||||
typealias Test2 = TC<Any, Collection<Any>>
|
typealias Test2 = TC<Any, Collection<Any>>
|
||||||
typealias Test3 = TCAlias<Any, Any>
|
typealias Test3 = <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION!>TCAlias<Any, Any><!>
|
||||||
typealias Test4 = TCAlias<Any, Collection<Any>>
|
typealias Test4 = TCAlias<Any, Collection<Any>>
|
||||||
typealias Test5 = TCAliasT<Any>
|
typealias Test5 = <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION!>TCAliasT<Any><!>
|
||||||
typealias Test6 = TCAliasC<Any>
|
typealias Test6 = <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION!>TCAliasC<Any><!>
|
||||||
typealias Test7 = TCAliasC<Collection<Any>>
|
typealias Test7 = TCAliasC<Collection<Any>>
|
||||||
typealias Test8 = TCAliasT1<Any>
|
typealias Test8 = <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION!>TCAliasT1<Any><!>
|
||||||
typealias Test9 = TCAliasC1<Any>
|
typealias Test9 = <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION!>TCAliasC1<Any><!>
|
||||||
typealias Test10 = TCAliasC1<Collection<Any>>
|
typealias Test10 = TCAliasC1<Collection<Any>>
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ interface Num<T : Number>
|
|||||||
|
|
||||||
typealias N<T> = Num<T>
|
typealias N<T> = Num<T>
|
||||||
|
|
||||||
class Test2<T : N<String>>
|
class Test2<T : <!UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION!>N<String><!>>
|
||||||
|
|||||||
Vendored
+1
-1
@@ -30,7 +30,7 @@ typealias N<T> = Num<T>
|
|||||||
|
|
||||||
val testN0 = N("")
|
val testN0 = N("")
|
||||||
val testN1 = N<Int>(1)
|
val testN1 = N<Int>(1)
|
||||||
val testN1a = N<String>("")
|
val testN1a = N<<!UPPER_BOUND_VIOLATED!>String<!>>("")
|
||||||
val testN2 = <!INAPPLICABLE_CANDIDATE!>N<!><Int, Int>(1)
|
val testN2 = <!INAPPLICABLE_CANDIDATE!>N<!><Int, Int>(1)
|
||||||
|
|
||||||
class MyPair<T1 : CharSequence, T2 : Number>(val string: T1, val number: T2)
|
class MyPair<T1 : CharSequence, T2 : Number>(val string: T1, val number: T2)
|
||||||
|
|||||||
+9
@@ -1230,6 +1230,15 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
add(FirErrors.UPPER_BOUND_VIOLATED) { firDiagnostic ->
|
add(FirErrors.UPPER_BOUND_VIOLATED) { firDiagnostic ->
|
||||||
UpperBoundViolatedImpl(
|
UpperBoundViolatedImpl(
|
||||||
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
|
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
|
||||||
|
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b),
|
||||||
|
firDiagnostic as FirPsiDiagnostic,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
add(FirErrors.UPPER_BOUND_VIOLATED_IN_TYPEALIAS_EXPANSION) { firDiagnostic ->
|
||||||
|
UpperBoundViolatedInTypealiasExpansionImpl(
|
||||||
|
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
|
||||||
|
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b),
|
||||||
firDiagnostic as FirPsiDiagnostic,
|
firDiagnostic as FirPsiDiagnostic,
|
||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
|
|||||||
+8
-1
@@ -872,7 +872,14 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
|
|
||||||
abstract class UpperBoundViolated : KtFirDiagnostic<PsiElement>() {
|
abstract class UpperBoundViolated : KtFirDiagnostic<PsiElement>() {
|
||||||
override val diagnosticClass get() = UpperBoundViolated::class
|
override val diagnosticClass get() = UpperBoundViolated::class
|
||||||
abstract val upperBound: KtType
|
abstract val expectedUpperBound: KtType
|
||||||
|
abstract val actualUpperBound: KtType
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class UpperBoundViolatedInTypealiasExpansion : KtFirDiagnostic<PsiElement>() {
|
||||||
|
override val diagnosticClass get() = UpperBoundViolatedInTypealiasExpansion::class
|
||||||
|
abstract val expectedUpperBound: KtType
|
||||||
|
abstract val actualUpperBound: KtType
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class TypeArgumentsNotAllowed : KtFirDiagnostic<PsiElement>() {
|
abstract class TypeArgumentsNotAllowed : KtFirDiagnostic<PsiElement>() {
|
||||||
|
|||||||
+11
-1
@@ -1403,13 +1403,23 @@ internal class ProjectionOnNonClassTypeArgumentImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class UpperBoundViolatedImpl(
|
internal class UpperBoundViolatedImpl(
|
||||||
override val upperBound: KtType,
|
override val expectedUpperBound: KtType,
|
||||||
|
override val actualUpperBound: KtType,
|
||||||
firDiagnostic: FirPsiDiagnostic,
|
firDiagnostic: FirPsiDiagnostic,
|
||||||
override val token: ValidityToken,
|
override val token: ValidityToken,
|
||||||
) : KtFirDiagnostic.UpperBoundViolated(), KtAbstractFirDiagnostic<PsiElement> {
|
) : KtFirDiagnostic.UpperBoundViolated(), KtAbstractFirDiagnostic<PsiElement> {
|
||||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class UpperBoundViolatedInTypealiasExpansionImpl(
|
||||||
|
override val expectedUpperBound: KtType,
|
||||||
|
override val actualUpperBound: KtType,
|
||||||
|
firDiagnostic: FirPsiDiagnostic,
|
||||||
|
override val token: ValidityToken,
|
||||||
|
) : KtFirDiagnostic.UpperBoundViolatedInTypealiasExpansion(), KtAbstractFirDiagnostic<PsiElement> {
|
||||||
|
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
internal class TypeArgumentsNotAllowedImpl(
|
internal class TypeArgumentsNotAllowedImpl(
|
||||||
firDiagnostic: FirPsiDiagnostic,
|
firDiagnostic: FirPsiDiagnostic,
|
||||||
override val token: ValidityToken,
|
override val token: ValidityToken,
|
||||||
|
|||||||
Reference in New Issue
Block a user