[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")
|
parameter<ConeKotlinType>("type")
|
||||||
}
|
}
|
||||||
val PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE by error<KtModifierListOwner>(PositioningStrategy.VARIANCE_MODIFIER)
|
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") {
|
val CONSTRUCTOR_PROBLEMS by object : DiagnosticGroup("Constructor problems") {
|
||||||
|
|||||||
@@ -146,6 +146,8 @@ object FirErrors {
|
|||||||
val CYCLIC_INHERITANCE_HIERARCHY by error0<PsiElement>()
|
val CYCLIC_INHERITANCE_HIERARCHY by error0<PsiElement>()
|
||||||
val EXPANDED_TYPE_CANNOT_BE_INHERITED by error1<KtTypeReference, ConeKotlinType>()
|
val EXPANDED_TYPE_CANNOT_BE_INHERITED by error1<KtTypeReference, ConeKotlinType>()
|
||||||
val PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE by error0<KtModifierListOwner>(SourceElementPositioningStrategies.VARIANCE_MODIFIER)
|
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
|
// Constructor problems
|
||||||
val CONSTRUCTOR_IN_OBJECT by error0<KtDeclaration>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
|
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.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
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.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.extractTypeRefAndSourceFromTypeArgument
|
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.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
|
||||||
@@ -21,7 +22,6 @@ import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
|||||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.name.StandardClassIds
|
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
object FirSupertypesChecker : FirClassChecker() {
|
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)
|
checkConflictingBounds(declaration, context, reporter)
|
||||||
checkTypeAliasBound(declaration, containingDeclaration, context, reporter)
|
checkTypeAliasBound(declaration, containingDeclaration, context, reporter)
|
||||||
checkDynamicBounds(declaration, context, reporter)
|
checkDynamicBounds(declaration, context, reporter)
|
||||||
|
checkInconsistentTypeParameterBounds(declaration, context, reporter)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkFinalUpperBounds(
|
private fun checkFinalUpperBounds(
|
||||||
@@ -89,7 +90,7 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
|||||||
// report the diagnostic on that bound
|
// report the diagnostic on that bound
|
||||||
|
|
||||||
//take TypeConstraint bounds only to report on the same point as old FE
|
//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()
|
bounds.filter { it.isInTypeConstraint() }.toSet()
|
||||||
}
|
}
|
||||||
val reportOn =
|
val reportOn =
|
||||||
@@ -154,5 +155,29 @@ object FirTypeParameterBoundsChecker : FirTypeParameterChecker() {
|
|||||||
private fun KotlinTypeMarker.isRelated(context: TypeCheckerProviderContext, type: KotlinTypeMarker?): Boolean =
|
private fun KotlinTypeMarker.isRelated(context: TypeCheckerProviderContext, type: KotlinTypeMarker?): Boolean =
|
||||||
isSubtypeOf(context, type) || isSupertypeOf(context, type)
|
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.RENDER_TYPE
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOL
|
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.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.TO_STRING
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.VARIABLE_NAME
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.VARIABLE_NAME
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.VISIBILITY
|
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_MODIFIERS
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INCOMPATIBLE_TYPES
|
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.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.INFERENCE_ERROR
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFIX_MODIFIER_REQUIRED
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INFIX_MODIFIER_REQUIRED
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION
|
||||||
@@ -466,6 +469,20 @@ class FirDefaultErrorMessages {
|
|||||||
RENDER_TYPE
|
RENDER_TYPE
|
||||||
)
|
)
|
||||||
map.put(PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE, "Projections are not allowed for immediate arguments of a supertype")
|
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
|
// Constructor problems
|
||||||
map.put(CONSTRUCTOR_IN_OBJECT, "Constructors are not allowed for objects")
|
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? ->
|
val TO_STRING = Renderer { element: Any? ->
|
||||||
element.toString()
|
element.toString()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
// FILE: a.kt
|
|
||||||
interface A<in T> {}
|
|
||||||
interface B<T> : A<Int> {}
|
|
||||||
interface C<T> : B<T>, A<T> {}
|
|
||||||
interface C1<T> : B<T>, A<Any> {}
|
|
||||||
interface D : C<Boolean>, B<Double>{}
|
|
||||||
|
|
||||||
interface A1<out T> {}
|
|
||||||
interface B1 : A1<Int> {}
|
|
||||||
interface B2 : A1<Any>, B1 {}
|
|
||||||
|
|
||||||
interface BA1<T> {}
|
|
||||||
interface BB1 : BA1<Int> {}
|
|
||||||
interface BB2 : BA1<Any>, BB1 {}
|
|
||||||
|
|
||||||
|
|
||||||
// FILE: b.kt
|
|
||||||
package x
|
|
||||||
interface AA1<out T> {}
|
|
||||||
interface AB1 : AA1<Int> {}
|
|
||||||
interface AB3 : AA1<Comparable<Int>> {}
|
|
||||||
interface AB2 : AA1<Number>, AB1, AB3 {}
|
|
||||||
|
|
||||||
// FILE: c.kt
|
|
||||||
package x2
|
|
||||||
interface AA1<out T> {}
|
|
||||||
interface AB1 : AA1<Any> {}
|
|
||||||
interface AB3 : AA1<Comparable<Int>> {}
|
|
||||||
interface AB2 : AA1<Number>, AB1, AB3 {}
|
|
||||||
|
|
||||||
// FILE: d.kt
|
|
||||||
package x3
|
|
||||||
interface AA1<in T> {}
|
|
||||||
interface AB1 : AA1<Any> {}
|
|
||||||
interface AB3 : AA1<Comparable<Int>> {}
|
|
||||||
interface AB2 : AA1<Number>, AB1, AB3 {}
|
|
||||||
|
|
||||||
// FILE: e.kt
|
|
||||||
package sx2
|
|
||||||
interface AA1<in T> {}
|
|
||||||
interface AB1 : AA1<Int> {}
|
|
||||||
interface AB3 : AA1<Comparable<Int>> {}
|
|
||||||
interface AB2 : AA1<Number>, AB1, AB3 {}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// FILE: a.kt
|
// FILE: a.kt
|
||||||
interface A<in T> {}
|
interface A<in T> {}
|
||||||
interface B<T> : A<Int> {}
|
interface B<T> : A<Int> {}
|
||||||
@@ -16,28 +17,36 @@ interface BB2 : <!INCONSISTENT_TYPE_PARAMETER_VALUES!>BA1<Any>, BB1<!> {}
|
|||||||
|
|
||||||
// FILE: b.kt
|
// FILE: b.kt
|
||||||
package x
|
package x
|
||||||
interface AA1<out T> {}
|
interface AA1<out T> {}
|
||||||
interface AB1 : AA1<Int> {}
|
interface AB1 : AA1<Int> {}
|
||||||
interface AB3 : AA1<Comparable<Int>> {}
|
interface AB3 : AA1<Comparable<Int>> {}
|
||||||
interface AB2 : <!INCONSISTENT_TYPE_PARAMETER_VALUES!>AA1<Number>, AB1, AB3<!> {}
|
interface AB2 : <!INCONSISTENT_TYPE_PARAMETER_VALUES!>AA1<Number>, AB1, AB3<!> {}
|
||||||
|
|
||||||
// FILE: c.kt
|
// FILE: c.kt
|
||||||
package x2
|
package x2
|
||||||
interface AA1<out T> {}
|
interface AA1<out T> {}
|
||||||
interface AB1 : AA1<Any> {}
|
interface AB1 : AA1<Any> {}
|
||||||
interface AB3 : AA1<Comparable<Int>> {}
|
interface AB3 : AA1<Comparable<Int>> {}
|
||||||
interface AB2 : <!INCONSISTENT_TYPE_PARAMETER_VALUES!>AA1<Number>, AB1, AB3<!> {}
|
interface AB2 : <!INCONSISTENT_TYPE_PARAMETER_VALUES!>AA1<Number>, AB1, AB3<!> {}
|
||||||
|
|
||||||
// FILE: d.kt
|
// FILE: d.kt
|
||||||
package x3
|
package x3
|
||||||
interface AA1<in T> {}
|
interface AA1<in T> {}
|
||||||
interface AB1 : AA1<Any> {}
|
interface AB1 : AA1<Any> {}
|
||||||
interface AB3 : AA1<Comparable<Int>> {}
|
interface AB3 : AA1<Comparable<Int>> {}
|
||||||
interface AB2 : <!INCONSISTENT_TYPE_PARAMETER_VALUES!>AA1<Number>, AB1, AB3<!> {}
|
interface AB2 : <!INCONSISTENT_TYPE_PARAMETER_VALUES!>AA1<Number>, AB1, AB3<!> {}
|
||||||
|
|
||||||
// FILE: e.kt
|
// FILE: e.kt
|
||||||
package sx2
|
package sx2
|
||||||
interface AA1<in T> {}
|
interface AA1<in T> {}
|
||||||
interface AB1 : AA1<Int> {}
|
interface AB1 : AA1<Int> {}
|
||||||
interface AB3 : AA1<Comparable<Int>> {}
|
interface AB3 : AA1<Comparable<Int>> {}
|
||||||
interface AB2 : <!INCONSISTENT_TYPE_PARAMETER_VALUES!>AA1<Number>, AB1, AB3<!> {}
|
interface AB2 : <!INCONSISTENT_TYPE_PARAMETER_VALUES!>AA1<Number>, AB1, AB3<!> {}
|
||||||
|
|
||||||
|
// FILE: f.kt
|
||||||
|
interface I0<T1, T2>
|
||||||
|
abstract class C2<T3, T4> : I0<T3, T4>
|
||||||
|
typealias TA<T5, T6> = C2<T6, T5>
|
||||||
|
interface I2
|
||||||
|
interface I3
|
||||||
|
class C3 : TA<I2, I3>(), I0<I3, I2>
|
||||||
@@ -60,12 +60,45 @@ public interface C1</*0*/ T> : B<T>, A<kotlin.Any> {
|
|||||||
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract class C2</*0*/ T3, /*1*/ T4> : I0<T3, T4> {
|
||||||
|
public constructor C2</*0*/ T3, /*1*/ T4>()
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class C3 : TA<I2, I3> /* = C2<I3, I2> */, I0<I3, I2> {
|
||||||
|
public constructor C3()
|
||||||
|
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
public interface D : C<kotlin.Boolean>, B<kotlin.Double> {
|
public interface D : C<kotlin.Boolean>, B<kotlin.Double> {
|
||||||
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface I0</*0*/ T1, /*1*/ T2> {
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface I2 {
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface I3 {
|
||||||
|
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||||
|
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||||
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
|
}
|
||||||
|
public typealias TA</*0*/ T5, /*1*/ T6> = C2<T6, T5>
|
||||||
|
|
||||||
package sx2 {
|
package sx2 {
|
||||||
|
|
||||||
public interface AA1</*0*/ in T> {
|
public interface AA1</*0*/ in T> {
|
||||||
@@ -173,3 +206,4 @@ package x3 {
|
|||||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-9
@@ -1,9 +0,0 @@
|
|||||||
interface A
|
|
||||||
interface B: A
|
|
||||||
interface D
|
|
||||||
|
|
||||||
interface BaseSuper<out T>
|
|
||||||
interface BaseImpl: BaseSuper<D>
|
|
||||||
interface DerivedSuper<out S>: BaseSuper<S>, BaseImpl
|
|
||||||
|
|
||||||
fun test(t: BaseSuper<B>) = t is DerivedSuper<A>
|
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
interface A
|
interface A
|
||||||
interface B: A
|
interface B: A
|
||||||
interface D
|
interface D
|
||||||
|
|||||||
-7
@@ -1,7 +0,0 @@
|
|||||||
interface A
|
|
||||||
interface B : A
|
|
||||||
|
|
||||||
interface ListA : List<A>
|
|
||||||
interface ListB : List<B>
|
|
||||||
|
|
||||||
interface Z<T> where T : ListA, T : ListB
|
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
interface A
|
interface A
|
||||||
interface B : A
|
interface B : A
|
||||||
|
|
||||||
|
|||||||
Vendored
-33
@@ -1,33 +0,0 @@
|
|||||||
interface IA {
|
|
||||||
fun method(): String
|
|
||||||
val propVal: String
|
|
||||||
var propVar: String
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IB1 : IA
|
|
||||||
interface IB2 : IA
|
|
||||||
|
|
||||||
interface IGA<T> {
|
|
||||||
fun method(): T
|
|
||||||
val propVal: T
|
|
||||||
var propVar: T
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IGB1Str : IGA<String>
|
|
||||||
interface IGB2Str : IGA<String>
|
|
||||||
interface IGB3Int : IGA<Int>
|
|
||||||
|
|
||||||
interface IGB4T<T> : IGA<T>
|
|
||||||
interface IGB5T<T> : IGA<T>
|
|
||||||
|
|
||||||
interface IC : IB1, IB2
|
|
||||||
|
|
||||||
interface IGC1 : IGB1Str, IGB2Str
|
|
||||||
|
|
||||||
interface IGC2 : IGB1Str, IGB3Int
|
|
||||||
|
|
||||||
interface IGC3<T> : IGB4T<T>, IGB5T<T>
|
|
||||||
|
|
||||||
interface IGC4<T> : IGB4T<T>, IGB5T<String>
|
|
||||||
|
|
||||||
interface IGC5 : IGB4T<String>, IGB5T<String>
|
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
interface IA {
|
interface IA {
|
||||||
fun method(): String
|
fun method(): String
|
||||||
val propVal: String
|
val propVal: String
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
interface X<T>
|
interface X<T>
|
||||||
interface A: X<String>
|
interface A: X<String>
|
||||||
interface B : A, X<Int>
|
interface B : <!INCONSISTENT_TYPE_PARAMETER_VALUES!>A, X<Int><!>
|
||||||
|
|
||||||
fun foo(x: B) {
|
fun foo(x: B) {
|
||||||
// Checks that when checking subtypes we search closes corresponding constructor (e.g. with BFS)
|
// Checks that when checking subtypes we search closes corresponding constructor (e.g. with BFS)
|
||||||
|
|||||||
+22
@@ -425,6 +425,28 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
add(FirErrors.INCONSISTENT_TYPE_PARAMETER_VALUES) { firDiagnostic ->
|
||||||
|
InconsistentTypeParameterValuesImpl(
|
||||||
|
firSymbolBuilder.classifierBuilder.buildTypeParameterSymbol(firDiagnostic.a.fir),
|
||||||
|
firSymbolBuilder.classifierBuilder.buildClassLikeSymbol(firDiagnostic.b.fir),
|
||||||
|
firDiagnostic.c.map { coneKotlinType ->
|
||||||
|
firSymbolBuilder.typeBuilder.buildKtType(coneKotlinType)
|
||||||
|
},
|
||||||
|
firDiagnostic as FirPsiDiagnostic,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
add(FirErrors.INCONSISTENT_TYPE_PARAMETER_BOUNDS) { firDiagnostic ->
|
||||||
|
InconsistentTypeParameterBoundsImpl(
|
||||||
|
firSymbolBuilder.classifierBuilder.buildTypeParameterSymbol(firDiagnostic.a.fir),
|
||||||
|
firSymbolBuilder.classifierBuilder.buildClassLikeSymbol(firDiagnostic.b.fir),
|
||||||
|
firDiagnostic.c.map { coneKotlinType ->
|
||||||
|
firSymbolBuilder.typeBuilder.buildKtType(coneKotlinType)
|
||||||
|
},
|
||||||
|
firDiagnostic as FirPsiDiagnostic,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
add(FirErrors.CONSTRUCTOR_IN_OBJECT) { firDiagnostic ->
|
add(FirErrors.CONSTRUCTOR_IN_OBJECT) { firDiagnostic ->
|
||||||
ConstructorInObjectImpl(
|
ConstructorInObjectImpl(
|
||||||
firDiagnostic as FirPsiDiagnostic,
|
firDiagnostic as FirPsiDiagnostic,
|
||||||
|
|||||||
+14
@@ -316,6 +316,20 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
override val diagnosticClass get() = ProjectionInImmediateArgumentToSupertype::class
|
override val diagnosticClass get() = ProjectionInImmediateArgumentToSupertype::class
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class InconsistentTypeParameterValues : KtFirDiagnostic<KtClass>() {
|
||||||
|
override val diagnosticClass get() = InconsistentTypeParameterValues::class
|
||||||
|
abstract val typeParameter: KtTypeParameterSymbol
|
||||||
|
abstract val type: KtClassLikeSymbol
|
||||||
|
abstract val bounds: List<KtType>
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class InconsistentTypeParameterBounds : KtFirDiagnostic<PsiElement>() {
|
||||||
|
override val diagnosticClass get() = InconsistentTypeParameterBounds::class
|
||||||
|
abstract val typeParameter: KtTypeParameterSymbol
|
||||||
|
abstract val type: KtClassLikeSymbol
|
||||||
|
abstract val bounds: List<KtType>
|
||||||
|
}
|
||||||
|
|
||||||
abstract class ConstructorInObject : KtFirDiagnostic<KtDeclaration>() {
|
abstract class ConstructorInObject : KtFirDiagnostic<KtDeclaration>() {
|
||||||
override val diagnosticClass get() = ConstructorInObject::class
|
override val diagnosticClass get() = ConstructorInObject::class
|
||||||
}
|
}
|
||||||
|
|||||||
+20
@@ -494,6 +494,26 @@ internal class ProjectionInImmediateArgumentToSupertypeImpl(
|
|||||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class InconsistentTypeParameterValuesImpl(
|
||||||
|
override val typeParameter: KtTypeParameterSymbol,
|
||||||
|
override val type: KtClassLikeSymbol,
|
||||||
|
override val bounds: List<KtType>,
|
||||||
|
firDiagnostic: FirPsiDiagnostic,
|
||||||
|
override val token: ValidityToken,
|
||||||
|
) : KtFirDiagnostic.InconsistentTypeParameterValues(), KtAbstractFirDiagnostic<KtClass> {
|
||||||
|
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class InconsistentTypeParameterBoundsImpl(
|
||||||
|
override val typeParameter: KtTypeParameterSymbol,
|
||||||
|
override val type: KtClassLikeSymbol,
|
||||||
|
override val bounds: List<KtType>,
|
||||||
|
firDiagnostic: FirPsiDiagnostic,
|
||||||
|
override val token: ValidityToken,
|
||||||
|
) : KtFirDiagnostic.InconsistentTypeParameterBounds(), KtAbstractFirDiagnostic<PsiElement> {
|
||||||
|
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
internal class ConstructorInObjectImpl(
|
internal class ConstructorInObjectImpl(
|
||||||
firDiagnostic: FirPsiDiagnostic,
|
firDiagnostic: FirPsiDiagnostic,
|
||||||
override val token: ValidityToken,
|
override val token: ValidityToken,
|
||||||
|
|||||||
+9
-9
@@ -1,42 +1,42 @@
|
|||||||
interface A<in T> {}
|
interface A<in T> {}
|
||||||
interface B<T> : A<Int> {}
|
interface B<T> : A<Int> {}
|
||||||
interface C<T> : B<T>, A<T> {}
|
interface C<T> : <error descr="[INCONSISTENT_TYPE_PARAMETER_VALUES] Type parameter T of 'A' has inconsistent values: kotlin/Int, T">B<T>, A<T></error> {}
|
||||||
interface C1<T> : B<T>, A<Any> {}
|
interface C1<T> : <error descr="[INCONSISTENT_TYPE_PARAMETER_VALUES] Type parameter T of 'A' has inconsistent values: kotlin/Int, kotlin/Any">B<T>, A<Any></error> {}
|
||||||
interface D : C<Boolean>, B<Double>{}
|
interface D : <error descr="[INCONSISTENT_TYPE_PARAMETER_VALUES] Type parameter T of 'A' has inconsistent values: kotlin/Int, kotlin/Boolean"><error descr="[INCONSISTENT_TYPE_PARAMETER_VALUES] Type parameter T of 'B' has inconsistent values: kotlin/Boolean, kotlin/Double">C<Boolean>, B<Double></error></error>{}
|
||||||
|
|
||||||
interface A1<out T> {}
|
interface A1<out T> {}
|
||||||
interface B1 : A1<Int> {}
|
interface B1 : A1<Int> {}
|
||||||
interface B2 : A1<Any>, B1 {}
|
interface B2 : <error descr="[INCONSISTENT_TYPE_PARAMETER_VALUES] Type parameter T of 'A1' has inconsistent values: kotlin/Any, kotlin/Int">A1<Any>, B1</error> {}
|
||||||
|
|
||||||
interface BA1<T> {}
|
interface BA1<T> {}
|
||||||
interface BB1 : BA1<Int> {}
|
interface BB1 : BA1<Int> {}
|
||||||
interface BB2 : BA1<Any>, BB1 {}
|
interface BB2 : <error descr="[INCONSISTENT_TYPE_PARAMETER_VALUES] Type parameter T of 'BA1' has inconsistent values: kotlin/Any, kotlin/Int">BA1<Any>, BB1</error> {}
|
||||||
|
|
||||||
|
|
||||||
//package x {
|
//package x {
|
||||||
interface xAA1<out T> {}
|
interface xAA1<out T> {}
|
||||||
interface xAB1 : xAA1<Int> {}
|
interface xAB1 : xAA1<Int> {}
|
||||||
interface xAB3 : xAA1<Comparable<Int>> {}
|
interface xAB3 : xAA1<Comparable<Int>> {}
|
||||||
interface xAB2 : xAA1<Number>, xAB1, xAB3 {}
|
interface xAB2 : <error descr="[INCONSISTENT_TYPE_PARAMETER_VALUES] Type parameter T of 'xAA1' has inconsistent values: kotlin/Number, kotlin/Int, kotlin/Comparable<kotlin/Int>">xAA1<Number>, xAB1, xAB3</error> {}
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//package x2 {
|
//package x2 {
|
||||||
interface x2AA1<out T> {}
|
interface x2AA1<out T> {}
|
||||||
interface x2AB1 : x2AA1<Any> {}
|
interface x2AB1 : x2AA1<Any> {}
|
||||||
interface x2AB3 : x2AA1<Comparable<Int>> {}
|
interface x2AB3 : x2AA1<Comparable<Int>> {}
|
||||||
interface x2AB2 : x2AA1<Number>, x2AB1, x2AB3 {}
|
interface x2AB2 : <error descr="[INCONSISTENT_TYPE_PARAMETER_VALUES] Type parameter T of 'x2AA1' has inconsistent values: kotlin/Number, kotlin/Any, kotlin/Comparable<kotlin/Int>">x2AA1<Number>, x2AB1, x2AB3</error> {}
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//package x3 {
|
//package x3 {
|
||||||
interface x3AA1<in T> {}
|
interface x3AA1<in T> {}
|
||||||
interface x3AB1 : x3AA1<Any> {}
|
interface x3AB1 : x3AA1<Any> {}
|
||||||
interface x3AB3 : x3AA1<Comparable<Int>> {}
|
interface x3AB3 : x3AA1<Comparable<Int>> {}
|
||||||
interface x3AB2 : x3AA1<Number>, x3AB1, x3AB3 {}
|
interface x3AB2 : <error descr="[INCONSISTENT_TYPE_PARAMETER_VALUES] Type parameter T of 'x3AA1' has inconsistent values: kotlin/Number, kotlin/Any, kotlin/Comparable<kotlin/Int>">x3AA1<Number>, x3AB1, x3AB3</error> {}
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//package sx2 {
|
//package sx2 {
|
||||||
interface sx2AA1<in T> {}
|
interface sx2AA1<in T> {}
|
||||||
interface sx2AB1 : sx2AA1<Int> {}
|
interface sx2AB1 : sx2AA1<Int> {}
|
||||||
interface sx2AB3 : sx2AA1<Comparable<Int>> {}
|
interface sx2AB3 : sx2AA1<Comparable<Int>> {}
|
||||||
interface sx2AB2 : sx2AA1<Number>, sx2AB1, sx2AB3 {}
|
interface sx2AB2 : <error descr="[INCONSISTENT_TYPE_PARAMETER_VALUES] Type parameter T of 'sx2AA1' has inconsistent values: kotlin/Number, kotlin/Int, kotlin/Comparable<kotlin/Int>">sx2AA1<Number>, sx2AB1, sx2AB3</error> {}
|
||||||
//}
|
//}
|
||||||
|
|||||||
Reference in New Issue
Block a user