Create VarianceChecker
This commit is contained in:
@@ -27,6 +27,7 @@ import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.InferenceErrorData;
|
||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
||||
import org.jetbrains.jet.lang.resolve.varianceChecker.VarianceChecker.VarianceConflictDiagnosticData;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lexer.JetKeywordToken;
|
||||
import org.jetbrains.jet.lexer.JetModifierKeywordToken;
|
||||
@@ -88,6 +89,8 @@ public interface Errors {
|
||||
DiagnosticFactory2<JetTypeReference, Integer, String> NO_TYPE_ARGUMENTS_ON_RHS = DiagnosticFactory2.create(ERROR);
|
||||
DiagnosticFactory1<JetTypeProjection, ClassifierDescriptor> CONFLICTING_PROJECTION = DiagnosticFactory1.create(ERROR, VARIANCE_IN_PROJECTION);
|
||||
DiagnosticFactory1<JetTypeProjection, ClassifierDescriptor> REDUNDANT_PROJECTION = DiagnosticFactory1.create(WARNING, VARIANCE_IN_PROJECTION);
|
||||
DiagnosticFactory1<PsiElement, VarianceConflictDiagnosticData> TYPE_VARIANCE_CONFLICT =
|
||||
DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
+16
@@ -28,9 +28,11 @@ import org.jetbrains.jet.lang.diagnostics.Errors;
|
||||
import org.jetbrains.jet.lang.psi.JetExpression;
|
||||
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
|
||||
import org.jetbrains.jet.lang.psi.JetTypeConstraint;
|
||||
import org.jetbrains.jet.lang.resolve.varianceChecker.VarianceChecker.VarianceConflictDiagnosticData;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lexer.JetKeywordToken;
|
||||
import org.jetbrains.jet.lexer.JetModifierKeywordToken;
|
||||
import org.jetbrains.jet.renderer.MultiRenderer;
|
||||
import org.jetbrains.jet.renderer.Renderer;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
@@ -388,6 +390,20 @@ public class DefaultErrorMessages {
|
||||
"Smart cast to ''{0}'' is impossible, because ''{1}'' could have changed since the is-check", RENDER_TYPE, STRING);
|
||||
|
||||
MAP.put(VARIANCE_ON_TYPE_PARAMETER_OF_FUNCTION_OR_PROPERTY, "Variance annotations are only allowed for type parameters of classes and traits");
|
||||
MAP.put(TYPE_VARIANCE_CONFLICT, "Type parameter {0} is declared as ''{1}'' but occurs in ''{2}'' position in type {3}",
|
||||
new MultiRenderer<VarianceConflictDiagnosticData>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public String[] render(@NotNull VarianceConflictDiagnosticData data) {
|
||||
return new String[] {
|
||||
NAME.render(data.getTypeParameter()),
|
||||
RENDER_POSITION_VARIANCE.render(data.getTypeParameter().getVariance()),
|
||||
RENDER_POSITION_VARIANCE.render(data.getOccurrencePosition()),
|
||||
RENDER_TYPE.render(data.getContainingType())
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
MAP.put(REDUNDANT_PROJECTION, "Projection is redundant: the corresponding type parameter of {0} has the same variance", NAME);
|
||||
MAP.put(CONFLICTING_PROJECTION, "Projection is conflicting with variance of the corresponding type parameter of {0}. Remove the projection or replace it with ''*''", NAME);
|
||||
|
||||
|
||||
@@ -131,6 +131,19 @@ public class Renderers {
|
||||
}
|
||||
};
|
||||
|
||||
public static final Renderer<Variance> RENDER_POSITION_VARIANCE = new Renderer<Variance>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public String render(@NotNull Variance variance) {
|
||||
switch (variance) {
|
||||
case INVARIANT: return "invariant";
|
||||
case IN_VARIANCE: return "in";
|
||||
case OUT_VARIANCE: return "out";
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown variance: " + variance);
|
||||
}
|
||||
};
|
||||
|
||||
public static final Renderer<Collection<? extends ResolvedCall<?>>> AMBIGUOUS_CALLS =
|
||||
new Renderer<Collection<? extends ResolvedCall<?>>>() {
|
||||
@NotNull
|
||||
|
||||
@@ -939,7 +939,7 @@ public class OverrideResolver {
|
||||
}
|
||||
|
||||
private void checkVisibility(@NotNull TopDownAnalysisContext c) {
|
||||
for (Map.Entry<JetDeclaration, CallableMemberDescriptor> entry : c.getMembers().entrySet()) {
|
||||
for (Map.Entry<JetCallableDeclaration, CallableMemberDescriptor> entry : c.getMembers().entrySet()) {
|
||||
checkVisibilityForMember(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public class TopDownAnalysisContext implements BodiesResolveContext {
|
||||
private final Map<JetNamedFunction, SimpleFunctionDescriptor> functions = Maps.newLinkedHashMap();
|
||||
private final Map<JetProperty, PropertyDescriptor> properties = Maps.newLinkedHashMap();
|
||||
private final Map<JetParameter, PropertyDescriptor> primaryConstructorParameterProperties = Maps.newHashMap();
|
||||
private Map<JetDeclaration, CallableMemberDescriptor> members = null;
|
||||
private Map<JetCallableDeclaration, CallableMemberDescriptor> members = null;
|
||||
|
||||
// File scopes - package scope extended with imports
|
||||
protected final Map<JetFile, WritableScope> fileScopes = Maps.newHashMap();
|
||||
@@ -173,7 +173,8 @@ public class TopDownAnalysisContext implements BodiesResolveContext {
|
||||
return functions;
|
||||
}
|
||||
|
||||
public Map<JetDeclaration, CallableMemberDescriptor> getMembers() {
|
||||
@NotNull
|
||||
public Map<JetCallableDeclaration, CallableMemberDescriptor> getMembers() {
|
||||
if (members == null) {
|
||||
members = Maps.newHashMap();
|
||||
members.putAll(functions);
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2010-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.lang.resolve.varianceChecker
|
||||
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
|
||||
import org.jetbrains.jet.lang.diagnostics.Errors
|
||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.jet.lang.types.Variance
|
||||
import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure.EnrichedProjectionKind.*
|
||||
import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure.*
|
||||
import org.jetbrains.jet.lang.descriptors.Visibilities
|
||||
import org.jetbrains.jet.lang.resolve.BindingTrace
|
||||
import org.jetbrains.jet.lang.resolve.typeBinding.TypeBinding
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.jet.lang.resolve.typeBinding.createTypeBinding
|
||||
import org.jetbrains.jet.lang.resolve.typeBinding.createTypeBindingForReturnType
|
||||
import org.jetbrains.jet.lang.psi.JetCallableDeclaration
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor
|
||||
import org.jetbrains.jet.lang.resolve.TopDownAnalysisContext
|
||||
import org.jetbrains.jet.lang.types.JetType
|
||||
import org.jetbrains.jet.lang.psi.JetClass
|
||||
import org.jetbrains.jet.lang.psi.JetTypeReference
|
||||
import org.jetbrains.jet.lang.types.Variance.*
|
||||
import org.jetbrains.jet.lang.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.jet.lang.descriptors.impl.FunctionDescriptorImpl
|
||||
import org.jetbrains.jet.lang.descriptors.impl.PropertyDescriptorImpl
|
||||
import org.jetbrains.jet.lang.descriptors.impl.PropertyAccessorDescriptorImpl
|
||||
import org.jetbrains.jet.lang.resolve.source.getPsi
|
||||
import org.jetbrains.jet.lang.descriptors.VariableDescriptor
|
||||
import org.jetbrains.jet.lang.psi.JetTypeParameterListOwner
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext
|
||||
import kotlin.platform.platformStatic
|
||||
|
||||
|
||||
class VarianceChecker(private val trace: BindingTrace) {
|
||||
|
||||
fun process(c: TopDownAnalysisContext) {
|
||||
for (member in c.getMembers().values()) {
|
||||
recordPrivateToThisIfNeeded(trace, member)
|
||||
}
|
||||
|
||||
check(c)
|
||||
}
|
||||
|
||||
fun check(c: TopDownAnalysisContext) {
|
||||
checkClasses(c)
|
||||
checkMembers(c)
|
||||
}
|
||||
|
||||
private fun checkClasses(c: TopDownAnalysisContext) {
|
||||
for (jetClassOrObject in c.getDeclaredClasses()!!.keySet()) {
|
||||
if (jetClassOrObject is JetClass) {
|
||||
for (specifier in jetClassOrObject.getDelegationSpecifiers()) {
|
||||
specifier.getTypeReference()?.checkTypePosition(trace.getBindingContext(), OUT_VARIANCE, trace)
|
||||
}
|
||||
jetClassOrObject.checkTypeParameters(trace.getBindingContext(), OUT_VARIANCE, trace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkMembers(c: TopDownAnalysisContext) {
|
||||
for ((declaration, descriptor) in c.getMembers()) {
|
||||
if (!Visibilities.isPrivate(descriptor.getVisibility())) {
|
||||
checkCallableDeclaration(trace.getBindingContext(), declaration, descriptor, trace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VarianceConflictDiagnosticData(
|
||||
val containingType: JetType,
|
||||
val typeParameter: TypeParameterDescriptor,
|
||||
val occurrencePosition: Variance
|
||||
)
|
||||
|
||||
class object {
|
||||
platformStatic fun recordPrivateToThisIfNeeded(trace: BindingTrace, descriptor: CallableMemberDescriptor) {
|
||||
if (descriptor.getVisibility() != Visibilities.PRIVATE) return
|
||||
|
||||
val psiElement = descriptor.getSource().getPsi()
|
||||
if (psiElement !is JetCallableDeclaration) return
|
||||
|
||||
if (!checkCallableDeclaration(trace.getBindingContext(), psiElement, descriptor, DiagnosticSink.DO_NOTHING)) {
|
||||
recordPrivateToThis(descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun recordPrivateToThis(descriptor: CallableDescriptor) {
|
||||
if (descriptor is FunctionDescriptorImpl) {
|
||||
descriptor.setVisibility(Visibilities.PRIVATE_TO_THIS);
|
||||
}
|
||||
else if (descriptor is PropertyDescriptorImpl) {
|
||||
descriptor.setVisibility(Visibilities.PRIVATE_TO_THIS);
|
||||
for (accessor in descriptor.getAccessors()) {
|
||||
(accessor as PropertyAccessorDescriptorImpl).setVisibility(Visibilities.PRIVATE_TO_THIS)
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw IllegalStateException("Unexpected descriptor type: ${descriptor.javaClass.getName()}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkCallableDeclaration(
|
||||
trace: BindingContext,
|
||||
declaration: JetCallableDeclaration,
|
||||
descriptor: CallableDescriptor,
|
||||
diagnosticSink: DiagnosticSink
|
||||
): Boolean {
|
||||
if (descriptor.getContainingDeclaration() !is ClassDescriptor) return true
|
||||
var noError = true
|
||||
|
||||
noError = noError and declaration.checkTypeParameters(trace, IN_VARIANCE, diagnosticSink)
|
||||
|
||||
noError = noError and declaration.getReceiverTypeReference()?.checkTypePosition(trace, IN_VARIANCE, diagnosticSink)
|
||||
|
||||
for (parameter in declaration.getValueParameters()) {
|
||||
noError = noError and parameter.getTypeReference()?.checkTypePosition(trace, IN_VARIANCE, diagnosticSink)
|
||||
}
|
||||
|
||||
val returnTypePosition = if (descriptor is VariableDescriptor && descriptor.isVar()) INVARIANT else OUT_VARIANCE
|
||||
noError = noError and declaration.createTypeBindingForReturnType(trace)?.checkTypePosition(returnTypePosition, diagnosticSink)
|
||||
|
||||
return noError
|
||||
}
|
||||
|
||||
private fun JetTypeParameterListOwner.checkTypeParameters(
|
||||
trace: BindingContext,
|
||||
typePosition: Variance,
|
||||
diagnosticSink: DiagnosticSink
|
||||
): Boolean {
|
||||
var noError = true
|
||||
for (typeParameter in getTypeParameters()) {
|
||||
noError = noError and typeParameter.getExtendsBound()?.checkTypePosition(trace, typePosition, diagnosticSink)
|
||||
}
|
||||
for (typeConstraint in getTypeConstraints()) {
|
||||
noError = noError and typeConstraint.getBoundTypeReference()?.checkTypePosition(trace, typePosition, diagnosticSink)
|
||||
}
|
||||
return noError
|
||||
}
|
||||
|
||||
private fun JetTypeReference.checkTypePosition(trace: BindingContext, position: Variance, diagnosticSink: DiagnosticSink)
|
||||
= createTypeBinding(trace)?.checkTypePosition(position, diagnosticSink)
|
||||
|
||||
private fun TypeBinding<PsiElement>.checkTypePosition(position: Variance, diagnosticSink: DiagnosticSink)
|
||||
= checkTypePosition(jetType, position, diagnosticSink)
|
||||
|
||||
private fun TypeBinding<PsiElement>.checkTypePosition(containingType: JetType, position: Variance, diagnosticSink: DiagnosticSink): Boolean {
|
||||
val classifierDescriptor = jetType.getConstructor().getDeclarationDescriptor()
|
||||
if (classifierDescriptor is TypeParameterDescriptor) {
|
||||
val declarationVariance = classifierDescriptor.getVariance()
|
||||
if (!declarationVariance.allowsPosition(position)) {
|
||||
diagnosticSink.report(
|
||||
Errors.TYPE_VARIANCE_CONFLICT.on(
|
||||
psiElement,
|
||||
VarianceConflictDiagnosticData(containingType, classifierDescriptor, position)
|
||||
)
|
||||
)
|
||||
}
|
||||
return declarationVariance.allowsPosition(position)
|
||||
}
|
||||
|
||||
var noError = true
|
||||
for (argumentBinding in getArgumentBindings()) {
|
||||
if (argumentBinding == null || argumentBinding.typeParameterDescriptor == null) continue
|
||||
|
||||
val projectionKind = getEffectiveProjectionKind(argumentBinding.typeParameterDescriptor, argumentBinding.typeProjection)!!
|
||||
val newPosition = when (projectionKind) {
|
||||
OUT -> position
|
||||
IN -> position.opposite()
|
||||
INV -> INVARIANT
|
||||
STAR -> null // CONFLICTING_PROJECTION error was reported
|
||||
}
|
||||
if (newPosition != null) {
|
||||
noError = noError and argumentBinding.typeBinding.checkTypePosition(containingType, newPosition, diagnosticSink)
|
||||
}
|
||||
}
|
||||
return noError
|
||||
}
|
||||
|
||||
private fun Boolean.and(other: Boolean?) = if (other == null) this else this and other
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,8 @@ public class Visibilities {
|
||||
}
|
||||
};
|
||||
|
||||
public static final Visibility PRIVATE_TO_THIS = PRIVATE;
|
||||
|
||||
public static final Visibility PROTECTED = new Visibility("protected", true) {
|
||||
@Override
|
||||
protected boolean isVisible(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
|
||||
@@ -174,4 +176,8 @@ public class Visibilities {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isPrivate(@NotNull Visibility visibility) {
|
||||
return visibility == PRIVATE || visibility == PRIVATE_TO_THIS;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user