FE 1.0: report TYPE_VARIANCE_CONFLICT also on qualifier type arguments

#KT-51439 Fixed
This commit is contained in:
Mikhail Glukhikh
2022-02-22 13:15:59 +03:00
committed by Space
parent fdb01d96f0
commit 7781413a93
15 changed files with 224 additions and 17 deletions
@@ -199,6 +199,8 @@ public interface Errors {
DiagnosticFactory1.create(WARNING, VARIANCE_IN_PROJECTION);
DiagnosticFactory1<PsiElement, VarianceConflictDiagnosticData> TYPE_VARIANCE_CONFLICT =
DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory1<PsiElement, VarianceConflictDiagnosticData> TYPE_VARIANCE_CONFLICT_WARNING =
DiagnosticFactory1.create(WARNING, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory1<PsiElement, VarianceConflictDiagnosticData> TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE =
DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory0<PsiElement> FINITE_BOUNDS_VIOLATION = DiagnosticFactory0.create(ERROR);
@@ -807,6 +807,9 @@ public class DefaultErrorMessages {
};
MAP.put(TYPE_VARIANCE_CONFLICT, "Type parameter {0} is declared as ''{1}'' but occurs in ''{2}'' position in type {3}",
varianceConflictDataRenderer);
MAP.put(TYPE_VARIANCE_CONFLICT_WARNING, "Type parameter {0} is declared as ''{1}'' but occurs in ''{2}'' position in type {3}. " +
"This diagnostic will become an error soon. See https://youtrack.jetbrains.com/issue/KT-50947",
varianceConflictDataRenderer);
MAP.put(TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE, "Type parameter {0} is declared as ''{1}'' but occurs in ''{2}'' position in abbreviated type {3}",
varianceConflictDataRenderer);
@@ -218,7 +218,7 @@ class LazyTopDownAnalyzer(
overrideResolver.check(c)
varianceChecker.check(c)
varianceChecker.check(c, languageVersionSettings)
declarationResolver.resolveAnnotationsOnFiles(c, fileScopeProvider)
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtTypeElement
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.psi.KtUserType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.*
@@ -30,6 +31,7 @@ interface TypeBinding<out P : PsiElement> {
val type: KotlinType
val isInAbbreviation: Boolean
val arguments: List<TypeArgumentBinding<P>?>
val isArgumentFromQualifier: Boolean get() = false
}
interface TypeArgumentBinding<out P : PsiElement> {
@@ -44,15 +46,20 @@ fun KtTypeReference.createTypeBinding(trace: BindingContext): TypeBinding<KtType
return if (type == null || psiElement == null)
null
else
createTypeBindingFromPsi(trace, psiElement, type)
createTypeBindingFromPsi(trace, psiElement, type, isArgumentFromQualifier = false)
}
private fun createTypeBindingFromPsi(trace: BindingContext, psiElement: KtTypeElement, type: KotlinType): TypeBinding<KtTypeElement> {
private fun createTypeBindingFromPsi(
trace: BindingContext,
psiElement: KtTypeElement,
type: KotlinType,
isArgumentFromQualifier: Boolean
): TypeBinding<KtTypeElement> {
val abbreviatedType = type.getAbbreviatedType()
return if (abbreviatedType != null)
AbbreviatedTypeBinding(type, psiElement)
AbbreviatedTypeBinding(type, psiElement, isArgumentFromQualifier)
else
ExplicitTypeBinding(trace, psiElement, type)
ExplicitTypeBinding(trace, psiElement, type, isArgumentFromQualifier)
}
fun KtCallableDeclaration.createTypeBindingForReturnType(trace: BindingContext): TypeBinding<PsiElement>? {
@@ -74,13 +81,20 @@ private class TypeArgumentBindingImpl<out P : PsiElement>(
private class ExplicitTypeBinding(
private val trace: BindingContext,
override val psiElement: KtTypeElement,
override val type: KotlinType
override val type: KotlinType,
override val isArgumentFromQualifier: Boolean
) : TypeBinding<KtTypeElement> {
override val isInAbbreviation: Boolean get() = false
override val arguments: List<TypeArgumentBinding<KtTypeElement>?>
get() {
val psiTypeArguments = psiElement.typeArgumentsAsTypes
val psiTypeArguments = psiElement.typeArgumentsAsTypes.toMutableList()
val qualifierArgumentStartIndex = psiTypeArguments.size
var current = psiElement
while (current is KtUserType) {
current = current.qualifier ?: break
psiTypeArguments += current.typeArgumentsAsTypes
}
assert(type.getAbbreviatedType() == null) { "Non-abbreviated type expected: $type" }
val isErrorBinding = run {
val sizeIsEqual = psiTypeArguments.size == type.arguments.size
@@ -90,16 +104,17 @@ private class ExplicitTypeBinding(
return psiTypeArguments.indices.map { index: Int ->
// todo fix for List<*>
val jetTypeReference = psiTypeArguments[index]
val jetTypeElement = jetTypeReference?.typeElement ?: return@map null
val typeReference = psiTypeArguments[index]
val typeElement = typeReference?.typeElement ?: return@map null
val isArgumentFromQualifier = index >= qualifierArgumentStartIndex
if (isErrorBinding) {
val nextJetType = trace[BindingContext.TYPE, jetTypeReference] ?: return@map null
val nextType = trace[BindingContext.TYPE, typeReference] ?: return@map null
return@map TypeArgumentBindingImpl(
TypeProjectionImpl(nextJetType),
TypeProjectionImpl(nextType),
null,
createTypeBindingFromPsi(trace, jetTypeElement, nextJetType)
createTypeBindingFromPsi(trace, typeElement, nextType, isArgumentFromQualifier)
)
}
@@ -107,7 +122,7 @@ private class ExplicitTypeBinding(
return@map TypeArgumentBindingImpl(
typeProjection,
type.constructor.parameters[index],
createTypeBindingFromPsi(trace, jetTypeElement, typeProjection.type)
createTypeBindingFromPsi(trace, typeElement, typeProjection.type, isArgumentFromQualifier)
)
}
}
@@ -115,13 +130,14 @@ private class ExplicitTypeBinding(
private class AbbreviatedTypeBinding(
override val type: KotlinType,
override val psiElement: KtTypeElement
override val psiElement: KtTypeElement,
override val isArgumentFromQualifier: Boolean
) : TypeBinding<KtTypeElement> {
override val isInAbbreviation: Boolean get() = true
override val arguments: List<TypeArgumentBinding<KtTypeElement>?>
get() = createTypeArgumentBindingsWithSinglePsiElement(type) { argumentType ->
AbbreviatedTypeBinding(argumentType, psiElement)
AbbreviatedTypeBinding(argumentType, psiElement, isArgumentFromQualifier)
}
}
@@ -18,6 +18,9 @@ package org.jetbrains.kotlin.resolve
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageFeature.ReportTypeVarianceConflictOnQualifierArguments
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyAccessorDescriptorImpl
@@ -43,7 +46,8 @@ class ManualVariance(val descriptor: TypeParameterDescriptor, val variance: Vari
class VarianceChecker(trace: BindingTrace) {
private val core = VarianceCheckerCore(trace.bindingContext, trace)
fun check(c: TopDownAnalysisContext) {
fun check(c: TopDownAnalysisContext, languageVersionSettings: LanguageVersionSettings) {
core.languageVersionSettings = languageVersionSettings
core.check(c)
}
}
@@ -59,6 +63,7 @@ class VarianceCheckerCore(
private val diagnosticSink: DiagnosticSink,
private val manualVariance: ManualVariance? = null
) {
internal var languageVersionSettings: LanguageVersionSettings? = null
fun check(c: TopDownAnalysisContext) {
checkClasses(c)
@@ -153,7 +158,17 @@ class VarianceCheckerCore(
) {
val varianceConflictDiagnosticData = VarianceConflictDiagnosticData(containingType, classifierDescriptor, position)
val diagnostic =
if (isInAbbreviation) Errors.TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE else Errors.TYPE_VARIANCE_CONFLICT
when {
isArgumentFromQualifier -> {
if (languageVersionSettings?.supportsFeature(ReportTypeVarianceConflictOnQualifierArguments) == true) {
Errors.TYPE_VARIANCE_CONFLICT
} else {
Errors.TYPE_VARIANCE_CONFLICT_WARNING
}
}
isInAbbreviation -> Errors.TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE
else -> Errors.TYPE_VARIANCE_CONFLICT
}
diagnosticSink.report(diagnostic.on(psiElement, varianceConflictDiagnosticData))
}
return declarationVariance.allowsPosition(position)