[FIR] Implement INCONSISTENT_TYPE_PARAMETER_VALUES, INCONSISTENT_TYPE_PARAMETER_BOUNDS
This commit is contained in:
committed by
teamcityserver
parent
ec20f52707
commit
92d7a61b4f
+10
@@ -140,6 +140,16 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
parameter<ConeKotlinType>("type")
|
||||
}
|
||||
val PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE by error<KtModifierListOwner>(PositioningStrategy.VARIANCE_MODIFIER)
|
||||
val INCONSISTENT_TYPE_PARAMETER_VALUES by error<KtClass>(PositioningStrategy.SUPERTYPES_LIST) {
|
||||
parameter<FirTypeParameterSymbol>("typeParameter")
|
||||
parameter<FirRegularClassSymbol>("type")
|
||||
parameter<Collection<ConeKotlinType>>("bounds")
|
||||
}
|
||||
val INCONSISTENT_TYPE_PARAMETER_BOUNDS by error<PsiElement> {
|
||||
parameter<FirTypeParameterSymbol>("typeParameter")
|
||||
parameter<FirRegularClassSymbol>("type")
|
||||
parameter<Collection<ConeKotlinType>>("bounds")
|
||||
}
|
||||
}
|
||||
|
||||
val CONSTRUCTOR_PROBLEMS by object : DiagnosticGroup("Constructor problems") {
|
||||
|
||||
@@ -146,6 +146,8 @@ object FirErrors {
|
||||
val CYCLIC_INHERITANCE_HIERARCHY by error0<PsiElement>()
|
||||
val EXPANDED_TYPE_CANNOT_BE_INHERITED by error1<KtTypeReference, ConeKotlinType>()
|
||||
val PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE by error0<KtModifierListOwner>(SourceElementPositioningStrategies.VARIANCE_MODIFIER)
|
||||
val INCONSISTENT_TYPE_PARAMETER_VALUES by error3<KtClass, FirTypeParameterSymbol, FirRegularClassSymbol, Collection<ConeKotlinType>>(SourceElementPositioningStrategies.SUPERTYPES_LIST)
|
||||
val INCONSISTENT_TYPE_PARAMETER_BOUNDS by error3<PsiElement, FirTypeParameterSymbol, FirRegularClassSymbol, Collection<ConeKotlinType>>()
|
||||
|
||||
// Constructor problems
|
||||
val CONSTRUCTOR_IN_OBJECT by error0<KtDeclaration>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.analysis.checkers
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.typeContext
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
|
||||
fun checkInconsistentTypeParameters(
|
||||
firTypeRefClasses: List<Pair<FirTypeRef?, FirRegularClass>>,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter,
|
||||
source: FirSourceElement?,
|
||||
isValues: Boolean
|
||||
) {
|
||||
val result = buildDeepSubstitutionMultimap(firTypeRefClasses, context)
|
||||
for ((typeParameterSymbol, typeAndProjections) in result) {
|
||||
val projections = typeAndProjections.projections
|
||||
if (projections.size > 1) {
|
||||
if (isValues) {
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirErrors.INCONSISTENT_TYPE_PARAMETER_VALUES,
|
||||
typeParameterSymbol,
|
||||
typeAndProjections.classSymbol,
|
||||
projections,
|
||||
context
|
||||
)
|
||||
} else {
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirErrors.INCONSISTENT_TYPE_PARAMETER_BOUNDS,
|
||||
typeParameterSymbol,
|
||||
typeAndProjections.classSymbol,
|
||||
projections,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildDeepSubstitutionMultimap(
|
||||
firTypeRefClasses: List<Pair<FirTypeRef?, FirRegularClass>>,
|
||||
context: CheckerContext,
|
||||
): Map<FirTypeParameterSymbol, ClassSymbolAndProjections> {
|
||||
val result = mutableMapOf<FirTypeParameterSymbol, ClassSymbolAndProjections>()
|
||||
val substitution = mutableMapOf<FirTypeParameterSymbol, ConeKotlinType>()
|
||||
val session = context.session
|
||||
val typeContext = session.typeContext
|
||||
|
||||
fun fillInDeepSubstitutor(typeArguments: Array<out ConeTypeProjection>?, firClass: FirRegularClass) {
|
||||
if (typeArguments != null) {
|
||||
val typeParameters = firClass.typeParameters
|
||||
val count = minOf(typeArguments.size, typeParameters.size)
|
||||
|
||||
for (index in 0 until count) {
|
||||
val typeArgument = typeArguments[index]
|
||||
|
||||
val substitutedArgument = ConeSubstitutorByMap(substitution, session).substituteArgument(typeArgument) ?: typeArgument
|
||||
val substitutedType = substitutedArgument.type ?: continue
|
||||
|
||||
val typeParameterSymbol = typeParameters[index].symbol
|
||||
|
||||
substitution[typeParameterSymbol] = substitutedType
|
||||
var classSymbolAndProjections = result[typeParameterSymbol]
|
||||
val projections: MutableList<ConeKotlinType>
|
||||
if (classSymbolAndProjections == null) {
|
||||
projections = mutableListOf()
|
||||
classSymbolAndProjections = ClassSymbolAndProjections(firClass.symbol, projections)
|
||||
result[typeParameterSymbol] = classSymbolAndProjections
|
||||
} else {
|
||||
projections = classSymbolAndProjections.projections
|
||||
}
|
||||
|
||||
if (projections.all {
|
||||
it != substitutedType && !AbstractTypeChecker.equalTypes(typeContext, it, substitutedType)
|
||||
}) {
|
||||
projections.add(substitutedType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (superTypeRef in firClass.superTypeRefs) {
|
||||
withSuppressedDiagnostics(superTypeRef, context) {
|
||||
val fullyExpandedType = superTypeRef.coneType.fullyExpandedType(session)
|
||||
val superTypeClass = fullyExpandedType.toRegularClass(session)
|
||||
if (!fullyExpandedType.isEnum && superTypeClass != null) {
|
||||
fillInDeepSubstitutor(fullyExpandedType.typeArguments, superTypeClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (firTypeRefClass in firTypeRefClasses) {
|
||||
fillInDeepSubstitutor(firTypeRefClass.first?.coneType?.fullyExpandedType(session)?.typeArguments, firTypeRefClass.second)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private data class ClassSymbolAndProjections(
|
||||
val classSymbol: FirRegularClassSymbol,
|
||||
val projections: MutableList<ConeKotlinType>
|
||||
)
|
||||
+6
-2
@@ -7,9 +7,10 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.checkInconsistentTypeParameters
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.extractTypeRefAndSourceFromTypeArgument
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isConflictingOrNotInvariant
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
@@ -21,7 +22,6 @@ import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
object FirSupertypesChecker : FirClassChecker() {
|
||||
@@ -108,5 +108,9 @@ object FirSupertypesChecker : FirClassChecker() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration is FirRegularClass && declaration.superTypeRefs.size > 1) {
|
||||
checkInconsistentTypeParameters(listOf(Pair(null, declaration)), context, reporter, declaration.source, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
-1
@@ -42,6 +42,7 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||
checkConflictingBounds(declaration, context, reporter)
|
||||
checkTypeAliasBound(declaration, containingDeclaration, context, reporter)
|
||||
checkDynamicBounds(declaration, context, reporter)
|
||||
checkInconsistentTypeParameterBounds(declaration, context, reporter)
|
||||
}
|
||||
|
||||
private fun checkFinalUpperBounds(
|
||||
@@ -89,7 +90,7 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||
// report the diagnostic on that bound
|
||||
|
||||
//take TypeConstraint bounds only to report on the same point as old FE
|
||||
val constraintBounds = with(SourceNavigator.forElement(declaration)){
|
||||
val constraintBounds = with(SourceNavigator.forElement(declaration)) {
|
||||
bounds.filter { it.isInTypeConstraint() }.toSet()
|
||||
}
|
||||
val reportOn =
|
||||
@@ -154,5 +155,29 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
||||
private fun KotlinTypeMarker.isRelated(context: TypeCheckerProviderContext, type: KotlinTypeMarker?): Boolean =
|
||||
isSubtypeOf(context, type) || isSupertypeOf(context, type)
|
||||
|
||||
private fun checkInconsistentTypeParameterBounds(
|
||||
declaration: FirTypeParameter,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
if (declaration.bounds.size <= 1) return
|
||||
|
||||
val firTypeRefClasses = mutableListOf<Pair<FirTypeRef, FirRegularClass>>()
|
||||
val firRegularClassesSet = mutableSetOf<FirRegularClass>()
|
||||
|
||||
for (bound in declaration.bounds) {
|
||||
val firRegularClass = bound.toRegularClass(context.session)
|
||||
if (firRegularClassesSet.contains(firRegularClass)) {
|
||||
// no need to throw INCONSISTENT_TYPE_PARAMETER_BOUNDS diagnostics here because REPEATED_BOUNDS diagnostic is already exist
|
||||
return
|
||||
}
|
||||
|
||||
if (firRegularClass != null) {
|
||||
firRegularClassesSet.add(firRegularClass)
|
||||
firTypeRefClasses.add(Pair(bound, firRegularClass))
|
||||
}
|
||||
}
|
||||
|
||||
checkInconsistentTypeParameters(firTypeRefClasses, context, reporter, declaration.source, false)
|
||||
}
|
||||
}
|
||||
|
||||
+17
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.REND
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_TYPE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOL
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOLS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.RENDER_COLLECTION_OF_TYPES
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.TO_STRING
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.VARIABLE_NAME
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.VISIBILITY
|
||||
@@ -171,6 +172,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_ENUM
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_MODIFIERS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_TYPES
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_TYPES_WARNING
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCONSISTENT_TYPE_PARAMETER_BOUNDS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCONSISTENT_TYPE_PARAMETER_VALUES
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFERENCE_ERROR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFIX_MODIFIER_REQUIRED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION
|
||||
@@ -466,6 +469,20 @@ class FirDefaultErrorMessages {
|
||||
RENDER_TYPE
|
||||
)
|
||||
map.put(PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE, "Projections are not allowed for immediate arguments of a supertype")
|
||||
map.put(
|
||||
INCONSISTENT_TYPE_PARAMETER_VALUES,
|
||||
"Type parameter {0} of ''{1}'' has inconsistent values: {2}",
|
||||
SYMBOL,
|
||||
SYMBOL,
|
||||
RENDER_COLLECTION_OF_TYPES
|
||||
)
|
||||
map.put(
|
||||
INCONSISTENT_TYPE_PARAMETER_BOUNDS,
|
||||
"Type parameter {0} of ''{1}'' has inconsistent bounds: {2}",
|
||||
SYMBOL,
|
||||
SYMBOL,
|
||||
RENDER_COLLECTION_OF_TYPES
|
||||
)
|
||||
|
||||
// Constructor problems
|
||||
map.put(CONSTRUCTOR_IN_OBJECT, "Constructors are not allowed for objects")
|
||||
|
||||
+6
@@ -38,6 +38,12 @@ object FirDiagnosticRenderers {
|
||||
}
|
||||
}
|
||||
|
||||
val RENDER_COLLECTION_OF_TYPES = Renderer { types: Collection<ConeKotlinType> ->
|
||||
types.joinToString(separator = ", ") { type ->
|
||||
RENDER_TYPE.render(type)
|
||||
}
|
||||
}
|
||||
|
||||
val TO_STRING = Renderer { element: Any? ->
|
||||
element.toString()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user