[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>>
|
||||
typealias NL<K> = NumColl<List<K>>
|
||||
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)
|
||||
val np1 = NumberPhile(10)
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
|
||||
import java.util.EnumMap
|
||||
|
||||
typealias SomeEnumMap = EnumMap<String, String?>
|
||||
typealias SomeEnumMap = EnumMap<<!UPPER_BOUND_VIOLATED!>String<!>, String?>
|
||||
|
||||
fun test(map: SomeEnumMap, qualifier: String?) {
|
||||
val result = map[qualifier]
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -402,7 +402,12 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
val INFERENCE_ERROR by error<PsiElement>()
|
||||
val PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT 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 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 INFERENCE_ERROR 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 WRONG_NUMBER_OF_TYPE_ARGUMENTS 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) {
|
||||
val delegatedTypeRef = typeRef.delegatedTypeRef
|
||||
if (delegatedTypeRef is FirUserTypeRef) {
|
||||
@@ -601,20 +601,22 @@ fun extractTypeRefAndSourceFromTypeArgument(typeRef: FirTypeRef?, index: Int): P
|
||||
|
||||
val typeArgument = currentTypeArguments?.elementAtOrNull(currentIndex)
|
||||
if (typeArgument is FirTypeProjectionWithVariance) {
|
||||
return Pair(typeArgument.typeRef, typeArgument.source)
|
||||
return FirTypeRefSource(typeArgument.typeRef, typeArgument.source)
|
||||
}
|
||||
} else if (delegatedTypeRef is FirFunctionTypeRef) {
|
||||
val valueParameters = delegatedTypeRef.valueParameters
|
||||
if (index < valueParameters.size) {
|
||||
val valueParamTypeRef = valueParameters.elementAt(index).returnTypeRef
|
||||
return Pair(valueParamTypeRef, valueParamTypeRef.source)
|
||||
return FirTypeRefSource(valueParamTypeRef, valueParamTypeRef.source)
|
||||
}
|
||||
if (index == valueParameters.size) {
|
||||
val returnTypeRef = delegatedTypeRef.returnTypeRef
|
||||
return Pair(returnTypeRef, returnTypeRef.source)
|
||||
return FirTypeRefSource(returnTypeRef, returnTypeRef.source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.fir.*
|
||||
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.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
@@ -155,11 +155,11 @@ object FirClassVarianceChecker : FirClassChecker() {
|
||||
}
|
||||
|
||||
if (newVariance != null) {
|
||||
val subTypeRefAndSource = extractTypeRefAndSourceFromTypeArgument(typeRef, index)
|
||||
val subTypeRefAndSource = extractArgumentTypeRefAndSource(typeRef, index)
|
||||
|
||||
checkVarianceConflict(
|
||||
typeArgumentType, newVariance, subTypeRefAndSource?.first, containingType,
|
||||
context, reporter, subTypeRefAndSource?.first?.source ?: source,
|
||||
typeArgumentType, newVariance, subTypeRefAndSource?.typeRef, containingType,
|
||||
context, reporter, subTypeRefAndSource?.typeRef?.source ?: source,
|
||||
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.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.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
@@ -77,11 +77,11 @@ object FirProjectionRelationChecker : FirBasicDeclarationChecker() {
|
||||
ProjectionRelation.None
|
||||
}
|
||||
|
||||
val (typeArgTypeRef, typeArgSource) = extractTypeRefAndSourceFromTypeArgument(typeRef, it) ?: continue
|
||||
val argTypeRefSource = extractArgumentTypeRefAndSource(typeRef, it) ?: continue
|
||||
|
||||
if (projectionRelation != ProjectionRelation.None && typeRef.source?.kind !is FirFakeSourceElementKind) {
|
||||
reporter.reportOn(
|
||||
typeArgSource ?: typeArgTypeRef.source,
|
||||
argTypeRefSource.source ?: argTypeRefSource.typeRef?.source,
|
||||
if (projectionRelation == ProjectionRelation.Conflicting)
|
||||
if (type != fullyExpandedType) FirErrors.CONFLICTING_PROJECTION_IN_TYPEALIAS_EXPANSION else FirErrors.CONFLICTING_PROJECTION
|
||||
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
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||
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.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.FirErrors
|
||||
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.FirResolvedNamedReference
|
||||
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.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
@@ -33,16 +35,20 @@ object FirUpperBoundViolatedClassChecker : FirBasicDeclarationChecker() {
|
||||
for (typeParameter in declaration.typeParameters) {
|
||||
if (typeParameter is FirTypeParameter) {
|
||||
for (bound in typeParameter.bounds) {
|
||||
analyzeTypeParameters(bound, context, reporter)
|
||||
checkUpperBoundViolated(bound, context, reporter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<*>) {
|
||||
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()
|
||||
}
|
||||
|
||||
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,
|
||||
context,
|
||||
reporter,
|
||||
calleeFir?.typeParameters?.map { it.symbol },
|
||||
expression.typeArguments
|
||||
typeParameters,
|
||||
typeArguments,
|
||||
typeArgumentRefsAndSources
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -81,12 +100,15 @@ object FirUpperBoundViolatedExpressionChecker : FirQualifiedAccessExpressionChec
|
||||
* Recursively analyzes type parameters and reports the diagnostic on the given source calculated using typeRef
|
||||
* Returns true if an error occurred
|
||||
*/
|
||||
private fun analyzeTypeParameters(
|
||||
private fun checkUpperBoundViolated(
|
||||
typeRef: FirTypeRef?,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter,
|
||||
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) {
|
||||
is ConeKotlinType -> typeRef
|
||||
@@ -101,10 +123,26 @@ private fun analyzeTypeParameters(
|
||||
|
||||
val typeParameterSymbols = typeParameters
|
||||
?: 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>()
|
||||
?: 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 }
|
||||
} else {
|
||||
listOf()
|
||||
@@ -142,36 +180,43 @@ private fun analyzeTypeParameters(
|
||||
typeArgumentTypeRef = localTypeArgument.typeRef
|
||||
typeArgument = typeArgumentTypeRef.coneType
|
||||
typeArgumentSource = localTypeArgument.source
|
||||
} else if (localTypeArgument is ConeKotlinType) {
|
||||
// Typealias case
|
||||
typeArgument = localTypeArgument
|
||||
typeArgumentSource = typeRef.source
|
||||
}
|
||||
} else {
|
||||
typeArgument = type.typeArguments[index] as? ConeKotlinType
|
||||
val argTypeRefSource = extractTypeRefAndSourceFromTypeArgument(typeRef, index)
|
||||
if (argTypeRefSource != null) {
|
||||
typeArgumentTypeRef = argTypeRefSource.first
|
||||
typeArgumentSource = argTypeRefSource.second
|
||||
val typeArgumentRefAndSource = typeArgumentRefsAndSources?.elementAtOrNull(index)
|
||||
if (typeArgumentRefAndSource != null) {
|
||||
typeArgumentTypeRef = typeArgumentRefAndSource.typeRef
|
||||
typeArgumentSource = typeArgumentRefAndSource.source
|
||||
} else {
|
||||
val extractedTypeArgumentRefAndSource = extractArgumentTypeRefAndSource(typeRef, index)
|
||||
if (extractedTypeArgumentRefAndSource != null) {
|
||||
typeArgumentTypeRef = extractedTypeArgumentRefAndSource.typeRef
|
||||
typeArgumentSource = extractedTypeArgumentRefAndSource.source
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeArgument != null && typeArgumentSource != null) {
|
||||
val upperBound = getSubstitutedUpperBound(typeParameterSymbols[index], substitutor, typeSystemContext)
|
||||
if (upperBound != null && !satisfiesBounds(upperBound, typeArgument.type, typeSystemContext)) {
|
||||
reporter.reportOn(typeArgumentSource, FirErrors.UPPER_BOUND_VIOLATED, upperBound, context)
|
||||
if (!isIgnoreTypeParameters || (typeArgument.typeArguments.isEmpty() && typeArgument !is ConeTypeParameterType)) {
|
||||
val intersection = typeSystemContext.intersectTypes(typeParameterSymbols[index].fir.bounds.map { it.coneType }) as? ConeKotlinType
|
||||
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.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_IN_TYPEALIAS_EXPANSION
|
||||
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_ELVIS
|
||||
@@ -649,7 +650,14 @@ class FirDefaultErrorMessages {
|
||||
map.put(RECURSION_IN_IMPLICIT_TYPES, "Recursion in implicit types")
|
||||
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(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(
|
||||
WRONG_NUMBER_OF_TYPE_ARGUMENTS,
|
||||
|
||||
@@ -22,6 +22,7 @@ abstract class AbstractConeSubstitutor(private val typeContext: ConeTypeContext)
|
||||
is ConeStarProjection -> old
|
||||
is ConeKotlinTypeProjectionIn -> ConeKotlinTypeProjectionIn(newType)
|
||||
is ConeKotlinTypeProjectionOut -> ConeKotlinTypeProjectionOut(newType)
|
||||
is ConeKotlinTypeConflictingProjection -> ConeKotlinTypeConflictingProjection(newType)
|
||||
is ConeKotlinType -> newType
|
||||
else -> old
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user