Refactoring: VarianceChecker / VarianceCheckerCore, check variance for a separate class

This commit is contained in:
Mikhail Glukhikh
2016-04-01 19:35:54 +03:00
parent 78a898651b
commit 7c02a1eaad
5 changed files with 151 additions and 131 deletions
@@ -30,7 +30,7 @@ import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData; import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.varianceChecker.VarianceChecker.VarianceConflictDiagnosticData; import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData;
import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.KotlinType;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.diagnostics.TypeMismatchDueToTypeProjectionsData;
import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.KtSimpleNameExpression; import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
import org.jetbrains.kotlin.psi.KtTypeConstraint; import org.jetbrains.kotlin.psi.KtTypeConstraint;
import org.jetbrains.kotlin.resolve.varianceChecker.VarianceChecker.VarianceConflictDiagnosticData; import org.jetbrains.kotlin.resolve.VarianceConflictDiagnosticData;
import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.util.MappedExtensionProvider; import org.jetbrains.kotlin.util.MappedExtensionProvider;
import org.jetbrains.kotlin.util.OperatorNameConventions; import org.jetbrains.kotlin.util.OperatorNameConventions;
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.lazy.* import org.jetbrains.kotlin.resolve.lazy.*
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor
import org.jetbrains.kotlin.resolve.varianceChecker.VarianceChecker
import java.util.* import java.util.*
class LazyTopDownAnalyzer( class LazyTopDownAnalyzer(
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.resolve.varianceChecker package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
@@ -23,18 +23,13 @@ import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure.EnrichedProjectionKind.* import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure.EnrichedProjectionKind.*
import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure.* import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure.*
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.typeBinding.TypeBinding import org.jetbrains.kotlin.resolve.typeBinding.TypeBinding
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.resolve.typeBinding.createTypeBinding import org.jetbrains.kotlin.resolve.typeBinding.createTypeBinding
import org.jetbrains.kotlin.resolve.typeBinding.createTypeBindingForReturnType import org.jetbrains.kotlin.resolve.typeBinding.createTypeBindingForReturnType
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.resolve.TopDownAnalysisContext
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.types.Variance.* import org.jetbrains.kotlin.types.Variance.*
import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
@@ -43,125 +38,130 @@ import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyAccessorDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.PropertyAccessorDescriptorImpl
import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.psi.KtTypeParameterListOwner import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
class ManualVariance(val descriptor: TypeParameterDescriptor, val variance: Variance)
class VarianceChecker(private val trace: BindingTrace) { class VarianceChecker(private val trace: BindingTrace) {
private val core = VarianceCheckerCore(trace.bindingContext, trace)
fun check(c: TopDownAnalysisContext) {
core.check(c)
}
}
class VarianceConflictDiagnosticData(
val containingType: KotlinType,
val typeParameter: TypeParameterDescriptor,
val occurrencePosition: Variance
)
class VarianceCheckerCore(
private val context: BindingContext,
private val diagnosticSink: DiagnosticSink,
private val manualVariance: ManualVariance? = null
) {
fun check(c: TopDownAnalysisContext) { fun check(c: TopDownAnalysisContext) {
checkClasses(c) checkClasses(c)
checkMembers(c) checkMembers(c)
} }
fun checkClassOrObject(klass: KtClassOrObject): Boolean {
if (klass is KtClass) {
if (!checkClassHeader(klass)) return false
}
for (member in klass.declarations) {
member as? KtCallableDeclaration ?: continue
val descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, member) as? CallableMemberDescriptor ?: continue
if (!checkMember(member, descriptor)) return false
}
return true
}
private fun checkClasses(c: TopDownAnalysisContext) { private fun checkClasses(c: TopDownAnalysisContext) {
for (jetClassOrObject in c.declaredClasses!!.keys) { for (jetClassOrObject in c.declaredClasses!!.keys) {
if (jetClassOrObject is KtClass) { if (jetClassOrObject is KtClass) {
for (specifier in jetClassOrObject.getSuperTypeListEntries()) { checkClassHeader(jetClassOrObject)
specifier.typeReference?.checkTypePosition(trace.bindingContext, OUT_VARIANCE, trace)
}
jetClassOrObject.checkTypeParameters(trace.bindingContext, OUT_VARIANCE, trace)
} }
} }
} }
private fun checkClassHeader(klass: KtClass): Boolean {
var noError = true
for (specifier in klass.getSuperTypeListEntries()) {
noError = noError and specifier.typeReference?.checkTypePosition(context, OUT_VARIANCE)
}
return noError and klass.checkTypeParameters(context, OUT_VARIANCE)
}
private fun checkMembers(c: TopDownAnalysisContext) { private fun checkMembers(c: TopDownAnalysisContext) {
for ((declaration, descriptor) in c.members) { for ((declaration, descriptor) in c.members) {
if (!Visibilities.isPrivate(descriptor.visibility)) { checkMember(declaration, descriptor)
checkCallableDeclaration(trace.bindingContext, declaration, descriptor, trace)
}
} }
} }
class VarianceConflictDiagnosticData( private fun checkMember(member: KtCallableDeclaration, descriptor: CallableMemberDescriptor) =
val containingType: KotlinType, Visibilities.isPrivate(descriptor.visibility) || checkCallableDeclaration(context, member, descriptor)
val typeParameter: TypeParameterDescriptor,
val occurrencePosition: Variance
)
companion object { private fun TypeParameterDescriptor.varianceWithManual() =
@JvmStatic if (manualVariance != null && this == manualVariance.descriptor) manualVariance.variance else variance
fun recordPrivateToThisIfNeeded(trace: BindingTrace, descriptor: CallableMemberDescriptor) {
fun recordPrivateToThisIfNeeded(descriptor: CallableMemberDescriptor) {
if (isIrrelevant(descriptor) || descriptor.visibility != Visibilities.PRIVATE) return if (isIrrelevant(descriptor) || descriptor.visibility != Visibilities.PRIVATE) return
val psiElement = descriptor.source.getPsi() val psiElement = descriptor.source.getPsi() as? KtCallableDeclaration ?: return
if (psiElement !is KtCallableDeclaration) return
if (!checkCallableDeclaration(trace.bindingContext, psiElement, descriptor, DiagnosticSink.DO_NOTHING)) { if (!checkCallableDeclaration(context, psiElement, descriptor)) {
recordPrivateToThis(descriptor) recordPrivateToThis(descriptor)
} }
} }
private fun isIrrelevant(descriptor: CallableDescriptor): Boolean {
val containingClass = descriptor.containingDeclaration
if (containingClass !is ClassDescriptor) return true
return containingClass.typeConstructor.parameters.all { it.variance == INVARIANT }
}
private fun recordPrivateToThis(descriptor: CallableMemberDescriptor) {
if (descriptor is FunctionDescriptorImpl) {
descriptor.visibility = Visibilities.PRIVATE_TO_THIS;
}
else if (descriptor is PropertyDescriptorImpl) {
descriptor.visibility = Visibilities.PRIVATE_TO_THIS;
for (accessor in descriptor.accessors) {
(accessor as PropertyAccessorDescriptorImpl).visibility = Visibilities.PRIVATE_TO_THIS
}
}
else {
throw IllegalStateException("Unexpected descriptor type: ${descriptor.javaClass.name}")
}
}
private fun checkCallableDeclaration( private fun checkCallableDeclaration(
trace: BindingContext, trace: BindingContext,
declaration: KtCallableDeclaration, declaration: KtCallableDeclaration,
descriptor: CallableDescriptor, descriptor: CallableDescriptor
diagnosticSink: DiagnosticSink
): Boolean { ): Boolean {
if (isIrrelevant(descriptor)) return true if (isIrrelevant(descriptor)) return true
var noError = true var noError = true
noError = noError and declaration.checkTypeParameters(trace, IN_VARIANCE, diagnosticSink) noError = noError and declaration.checkTypeParameters(trace, IN_VARIANCE)
noError = noError and declaration.receiverTypeReference?.checkTypePosition(trace, IN_VARIANCE, diagnosticSink) noError = noError and declaration.receiverTypeReference?.checkTypePosition(trace, IN_VARIANCE)
for (parameter in declaration.valueParameters) { for (parameter in declaration.valueParameters) {
noError = noError and parameter.typeReference?.checkTypePosition(trace, IN_VARIANCE, diagnosticSink) noError = noError and parameter.typeReference?.checkTypePosition(trace, IN_VARIANCE)
} }
val returnTypePosition = if (descriptor is VariableDescriptor && descriptor.isVar) INVARIANT else OUT_VARIANCE val returnTypePosition = if (descriptor is VariableDescriptor && descriptor.isVar) INVARIANT else OUT_VARIANCE
noError = noError and declaration.createTypeBindingForReturnType(trace)?.checkTypePosition(returnTypePosition, diagnosticSink) noError = noError and declaration.createTypeBindingForReturnType(trace)?.checkTypePosition(returnTypePosition)
return noError return noError
} }
private fun KtTypeParameterListOwner.checkTypeParameters( private fun KtTypeParameterListOwner.checkTypeParameters(
trace: BindingContext, trace: BindingContext,
typePosition: Variance, typePosition: Variance
diagnosticSink: DiagnosticSink
): Boolean { ): Boolean {
var noError = true var noError = true
for (typeParameter in typeParameters) { for (typeParameter in typeParameters) {
noError = noError and typeParameter.extendsBound?.checkTypePosition(trace, typePosition, diagnosticSink) noError = noError and typeParameter.extendsBound?.checkTypePosition(trace, typePosition)
} }
for (typeConstraint in typeConstraints) { for (typeConstraint in typeConstraints) {
noError = noError and typeConstraint.boundTypeReference?.checkTypePosition(trace, typePosition, diagnosticSink) noError = noError and typeConstraint.boundTypeReference?.checkTypePosition(trace, typePosition)
} }
return noError return noError
} }
private fun KtTypeReference.checkTypePosition(trace: BindingContext, position: Variance, diagnosticSink: DiagnosticSink) private fun KtTypeReference.checkTypePosition(trace: BindingContext, position: Variance)
= createTypeBinding(trace)?.checkTypePosition(position, diagnosticSink) = createTypeBinding(trace)?.checkTypePosition(position)
private fun TypeBinding<PsiElement>.checkTypePosition(position: Variance, diagnosticSink: DiagnosticSink) private fun TypeBinding<PsiElement>.checkTypePosition(position: Variance) = checkTypePosition(kotlinType, position)
= checkTypePosition(kotlinType, position, diagnosticSink)
private fun TypeBinding<PsiElement>.checkTypePosition(containingType: KotlinType, position: Variance, diagnosticSink: DiagnosticSink): Boolean { private fun TypeBinding<PsiElement>.checkTypePosition(containingType: KotlinType, position: Variance): Boolean {
val classifierDescriptor = kotlinType.constructor.declarationDescriptor val classifierDescriptor = kotlinType.constructor.declarationDescriptor
if (classifierDescriptor is TypeParameterDescriptor) { if (classifierDescriptor is TypeParameterDescriptor) {
val declarationVariance = classifierDescriptor.variance val declarationVariance = classifierDescriptor.varianceWithManual()
if (!declarationVariance.allowsPosition(position) if (!declarationVariance.allowsPosition(position)
&& !kotlinType.annotations.hasAnnotation(KotlinBuiltIns.FQ_NAMES.unsafeVariance)) { && !kotlinType.annotations.hasAnnotation(KotlinBuiltIns.FQ_NAMES.unsafeVariance)) {
diagnosticSink.report( diagnosticSink.report(
@@ -186,12 +186,32 @@ class VarianceChecker(private val trace: BindingTrace) {
STAR -> null // CONFLICTING_PROJECTION error was reported STAR -> null // CONFLICTING_PROJECTION error was reported
} }
if (newPosition != null) { if (newPosition != null) {
noError = noError and argumentBinding.typeBinding.checkTypePosition(containingType, newPosition, diagnosticSink) noError = noError and argumentBinding.typeBinding.checkTypePosition(containingType, newPosition)
} }
} }
return noError return noError
} }
private fun isIrrelevant(descriptor: CallableDescriptor): Boolean {
val containingClass = descriptor.containingDeclaration as? ClassDescriptor ?: return true
return containingClass.typeConstructor.parameters.all { it.varianceWithManual() == INVARIANT }
}
companion object {
private fun recordPrivateToThis(descriptor: CallableMemberDescriptor) {
when (descriptor) {
is FunctionDescriptorImpl -> descriptor.visibility = Visibilities.PRIVATE_TO_THIS
is PropertyDescriptorImpl -> {
descriptor.visibility = Visibilities.PRIVATE_TO_THIS;
for (accessor in descriptor.accessors) {
(accessor as PropertyAccessorDescriptorImpl).visibility = Visibilities.PRIVATE_TO_THIS
}
}
else -> throw IllegalStateException("Unexpected descriptor type: ${descriptor.javaClass.name}")
}
}
private infix fun Boolean.and(other: Boolean?) = if (other == null) this else this and other private infix fun Boolean.and(other: Boolean?) = if (other == null) this else this and other
} }
} }
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DELEGATION import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DELEGATION
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.FAKE_OVERRIDE import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.FAKE_OVERRIDE
import org.jetbrains.kotlin.descriptors.impl.ConstructorDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ConstructorDescriptorImpl
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
@@ -37,7 +38,7 @@ import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProv
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.varianceChecker.VarianceChecker import org.jetbrains.kotlin.resolve.VarianceCheckerCore
import org.jetbrains.kotlin.storage.NotNullLazyValue import org.jetbrains.kotlin.storage.NotNullLazyValue
import org.jetbrains.kotlin.storage.NullableLazyValue import org.jetbrains.kotlin.storage.NullableLazyValue
import org.jetbrains.kotlin.types.DeferredType import org.jetbrains.kotlin.types.DeferredType
@@ -192,7 +193,7 @@ open class LazyClassMemberScope(
if (descriptor.kind != FAKE_OVERRIDE && descriptor.kind != DELEGATION) { if (descriptor.kind != FAKE_OVERRIDE && descriptor.kind != DELEGATION) {
OverridingUtil.resolveUnknownVisibilityForMember(descriptor, OverrideResolver.createCannotInferVisibilityReporter(trace)) OverridingUtil.resolveUnknownVisibilityForMember(descriptor, OverrideResolver.createCannotInferVisibilityReporter(trace))
} }
VarianceChecker.recordPrivateToThisIfNeeded(trace, descriptor); VarianceCheckerCore(trace.bindingContext, DiagnosticSink.DO_NOTHING).recordPrivateToThisIfNeeded(descriptor);
} }
} }