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.inference.InferenceErrorData;
|
||||||
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
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.lang.types.JetType;
|
||||||
import org.jetbrains.jet.lexer.JetKeywordToken;
|
import org.jetbrains.jet.lexer.JetKeywordToken;
|
||||||
import org.jetbrains.jet.lexer.JetModifierKeywordToken;
|
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);
|
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> CONFLICTING_PROJECTION = DiagnosticFactory1.create(ERROR, VARIANCE_IN_PROJECTION);
|
||||||
DiagnosticFactory1<JetTypeProjection, ClassifierDescriptor> REDUNDANT_PROJECTION = DiagnosticFactory1.create(WARNING, 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.JetExpression;
|
||||||
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
|
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
|
||||||
import org.jetbrains.jet.lang.psi.JetTypeConstraint;
|
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.lang.types.JetType;
|
||||||
import org.jetbrains.jet.lexer.JetKeywordToken;
|
import org.jetbrains.jet.lexer.JetKeywordToken;
|
||||||
import org.jetbrains.jet.lexer.JetModifierKeywordToken;
|
import org.jetbrains.jet.lexer.JetModifierKeywordToken;
|
||||||
|
import org.jetbrains.jet.renderer.MultiRenderer;
|
||||||
import org.jetbrains.jet.renderer.Renderer;
|
import org.jetbrains.jet.renderer.Renderer;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
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);
|
"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(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(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);
|
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 =
|
public static final Renderer<Collection<? extends ResolvedCall<?>>> AMBIGUOUS_CALLS =
|
||||||
new Renderer<Collection<? extends ResolvedCall<?>>>() {
|
new Renderer<Collection<? extends ResolvedCall<?>>>() {
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -939,7 +939,7 @@ public class OverrideResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void checkVisibility(@NotNull TopDownAnalysisContext c) {
|
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());
|
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<JetNamedFunction, SimpleFunctionDescriptor> functions = Maps.newLinkedHashMap();
|
||||||
private final Map<JetProperty, PropertyDescriptor> properties = Maps.newLinkedHashMap();
|
private final Map<JetProperty, PropertyDescriptor> properties = Maps.newLinkedHashMap();
|
||||||
private final Map<JetParameter, PropertyDescriptor> primaryConstructorParameterProperties = Maps.newHashMap();
|
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
|
// File scopes - package scope extended with imports
|
||||||
protected final Map<JetFile, WritableScope> fileScopes = Maps.newHashMap();
|
protected final Map<JetFile, WritableScope> fileScopes = Maps.newHashMap();
|
||||||
@@ -173,7 +173,8 @@ public class TopDownAnalysisContext implements BodiesResolveContext {
|
|||||||
return functions;
|
return functions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<JetDeclaration, CallableMemberDescriptor> getMembers() {
|
@NotNull
|
||||||
|
public Map<JetCallableDeclaration, CallableMemberDescriptor> getMembers() {
|
||||||
if (members == null) {
|
if (members == null) {
|
||||||
members = Maps.newHashMap();
|
members = Maps.newHashMap();
|
||||||
members.putAll(functions);
|
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) {
|
public static final Visibility PROTECTED = new Visibility("protected", true) {
|
||||||
@Override
|
@Override
|
||||||
protected boolean isVisible(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
|
protected boolean isVisible(@NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) {
|
||||||
@@ -174,4 +176,8 @@ public class Visibilities {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isPrivate(@NotNull Visibility visibility) {
|
||||||
|
return visibility == PRIVATE || visibility == PRIVATE_TO_THIS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user