Using of enum entries as types is now forbidden #KT-5401 Fixed

This commit is contained in:
Mikhail Glukhikh
2015-11-23 18:43:27 +03:00
parent 736813c76a
commit b9e9c47b28
12 changed files with 183 additions and 21 deletions
@@ -655,6 +655,7 @@ public interface Errors {
DiagnosticFactory0<KtSimpleNameExpression> CAST_NEVER_SUCCEEDS = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<KtTypeReference> DYNAMIC_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeReference> IS_ENUM_ENTRY = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtTypeReference> ENUM_ENTRY_AS_TYPE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<KtExpression, KotlinType> IMPLICIT_CAST_TO_UNIT_OR_ANY = DiagnosticFactory1.create(WARNING);
@@ -320,6 +320,7 @@ public class DefaultErrorMessages {
MAP.put(CAST_NEVER_SUCCEEDS, "This cast can never succeed");
MAP.put(DYNAMIC_NOT_ALLOWED, "Dynamic types are not allowed in this position");
MAP.put(IS_ENUM_ENTRY, "'is' over enum entry is not allowed, use comparison instead");
MAP.put(ENUM_ENTRY_AS_TYPE, "Use of enum entry names as types is not allowed, use enum type instead");
MAP.put(USELESS_NULLABLE_CHECK, "Non-null type is checked for instance of nullable type");
MAP.put(WRONG_SETTER_PARAMETER_TYPE, "Setter parameter type must be equal to the type of the property, i.e. ''{0}''", RENDER_TYPE, RENDER_TYPE);
MAP.put(WRONG_GETTER_RETURN_TYPE, "Getter return type must be equal to the type of the property, i.e. ''{0}''", RENDER_TYPE, RENDER_TYPE);
@@ -141,7 +141,8 @@ public class BodyResolver {
new Function1<LexicalScope, DataFlowInfo>() {
@Override
public DataFlowInfo invoke(@NotNull LexicalScope headerInnerScope) {
return resolveSecondaryConstructorDelegationCall(outerDataFlowInfo, trace, headerInnerScope, constructor, descriptor,
return resolveSecondaryConstructorDelegationCall(outerDataFlowInfo, trace, headerInnerScope,
constructor, descriptor,
callChecker);
}
},
@@ -187,9 +188,9 @@ public class BodyResolver {
// if next delegation call is super or primary constructor or already visited
if (!constructorDescriptor.getContainingDeclaration().equals(delegatedConstructorDescriptor.getContainingDeclaration()) ||
delegatedConstructorDescriptor.isPrimary() ||
visitedConstructors.contains(delegatedConstructorDescriptor)) {
break;
delegatedConstructorDescriptor.isPrimary() ||
visitedConstructors.contains(delegatedConstructorDescriptor)) {
break;
}
if (visitedInCurrentChain.contains(delegatedConstructorDescriptor)) {
@@ -214,7 +215,8 @@ public class BodyResolver {
currentConstructor = getDelegatedConstructor(currentConstructor);
assert currentConstructor != null : "Delegated constructor should not be null in cycle";
} while (startConstructor != currentConstructor);
}
while (startConstructor != currentConstructor);
}
@Nullable
@@ -251,7 +253,8 @@ public class BodyResolver {
@NotNull LexicalScope scopeForSupertypeResolution,
@NotNull final LexicalScope scopeForMemberResolution
) {
final LexicalScope scopeForConstructor = primaryConstructor == null
final LexicalScope scopeForConstructor =
primaryConstructor == null
? null
: FunctionDescriptorUtil.getFunctionInnerScope(scopeForSupertypeResolution, primaryConstructor, trace);
final ExpressionTypingServices typeInferrer = expressionTypingServices; // TODO : flow
@@ -477,7 +480,9 @@ public class BodyResolver {
}
if (classDescriptor != null && classDescriptor.getKind().isSingleton()) {
trace.report(SINGLETON_IN_SUPERTYPE.on(typeReference));
if (!DescriptorUtils.isEnumEntry(classDescriptor)) {
trace.report(SINGLETON_IN_SUPERTYPE.on(typeReference));
}
}
else if (constructor.isFinal() && !allowedFinalSupertypes.contains(constructor)) {
if (classDescriptor.getModality() == Modality.SEALED) {
@@ -565,7 +570,8 @@ public class BodyResolver {
new Function1<LexicalScopeImpl.InitializeHandler, Unit>() {
@Override
public Unit invoke(LexicalScopeImpl.InitializeHandler handler) {
for (ValueParameterDescriptor valueParameterDescriptor : unsubstitutedPrimaryConstructor.getValueParameters()) {
for (ValueParameterDescriptor
valueParameterDescriptor : unsubstitutedPrimaryConstructor.getValueParameters()) {
handler.addVariableDescriptor(valueParameterDescriptor);
}
return Unit.INSTANCE$;
@@ -662,11 +668,19 @@ public class BodyResolver {
}
private ObservableBindingTrace createFieldTrackingTrace(final PropertyDescriptor propertyDescriptor) {
return new ObservableBindingTrace(trace).addHandler(BindingContext.REFERENCE_TARGET, new ObservableBindingTrace.RecordHandler<KtReferenceExpression, DeclarationDescriptor>() {
return new ObservableBindingTrace(trace).addHandler(
BindingContext.REFERENCE_TARGET,
new ObservableBindingTrace.RecordHandler<KtReferenceExpression, DeclarationDescriptor>() {
@Override
public void handleRecord(WritableSlice<KtReferenceExpression, DeclarationDescriptor> slice, KtReferenceExpression expression, DeclarationDescriptor descriptor) {
if (expression instanceof KtSimpleNameExpression && descriptor instanceof SyntheticFieldDescriptor) {
trace.record(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor);
public void handleRecord(
WritableSlice<KtReferenceExpression, DeclarationDescriptor> slice,
KtReferenceExpression expression,
DeclarationDescriptor descriptor
) {
if (expression instanceof KtSimpleNameExpression &&
descriptor instanceof SyntheticFieldDescriptor) {
trace.record(BindingContext.BACKING_FIELD_REQUIRED,
propertyDescriptor);
}
}
});
@@ -858,7 +872,11 @@ public class BodyResolver {
final Queue<DeferredType> queue = new Queue<DeferredType>(deferredTypes.size() + 1);
trace.addHandler(DEFERRED_TYPE, new ObservableBindingTrace.RecordHandler<Box<DeferredType>, Boolean>() {
@Override
public void handleRecord(WritableSlice<Box<DeferredType>, Boolean> deferredTypeKeyDeferredTypeWritableSlice, Box<DeferredType> key, Boolean value) {
public void handleRecord(
WritableSlice<Box<DeferredType>, Boolean> deferredTypeKeyDeferredTypeWritableSlice,
Box<DeferredType> key,
Boolean value
) {
queue.addLast(key.getData());
}
});
@@ -37,6 +37,35 @@ import org.jetbrains.kotlin.resolve.BindingContext.TYPE_PARAMETER
import org.jetbrains.kotlin.resolve.DescriptorUtils.classCanHaveAbstractMembers
import org.jetbrains.kotlin.resolve.DescriptorUtils.classCanHaveOpenMembers
fun KtDeclaration.checkTypeReferences(trace: BindingTrace) {
if (this is KtCallableDeclaration) {
typeReference?.checkNotEnumEntry(trace)
receiverTypeReference?.checkNotEnumEntry(trace)
}
if (this is KtDeclarationWithBody) {
for (parameter in valueParameters) {
parameter.typeReference?.checkNotEnumEntry(trace)
}
}
}
fun KtTypeReference.checkNotEnumEntry(trace: BindingTrace): Boolean {
var result = false
trace.bindingContext.get(TYPE, this)?.let {
val targetDescriptor = TypeUtils.getClassDescriptor(it)
if (targetDescriptor != null && DescriptorUtils.isEnumEntry(targetDescriptor)) {
trace.report(ENUM_ENTRY_AS_TYPE.on(this))
result = true
}
}
typeElement?.let {
for (typeArgument in it.typeArgumentsAsTypes) {
typeArgument?.checkNotEnumEntry(trace)
}
}
return result
}
class DeclarationsChecker(
private val descriptorResolver: DescriptorResolver,
modifiersChecker: ModifiersChecker,
@@ -46,6 +75,8 @@ class DeclarationsChecker(
private val modifiersChecker = modifiersChecker.withTrace(trace)
fun KtDeclaration.checkTypeReferences() = checkTypeReferences(trace)
fun process(bodiesResolveContext: BodiesResolveContext) {
for (file in bodiesResolveContext.files) {
checkModifiersAndAnnotationsInPackageDirective(file)
@@ -69,6 +100,7 @@ class DeclarationsChecker(
checkPrimaryConstructor(classOrObject, classDescriptor)
classOrObject.checkTypeReferences()
modifiersChecker.checkModifiersForDeclaration(classOrObject, classDescriptor)
identifierChecker.checkDeclaration(classOrObject, trace)
checkClassExposedType(classOrObject, classDescriptor)
@@ -76,12 +108,14 @@ class DeclarationsChecker(
for ((function, functionDescriptor) in bodiesResolveContext.functions.entries) {
checkFunction(function, functionDescriptor)
function.checkTypeReferences()
modifiersChecker.checkModifiersForDeclaration(function, functionDescriptor)
identifierChecker.checkDeclaration(function, trace)
}
for ((property, propertyDescriptor) in bodiesResolveContext.properties.entries) {
checkProperty(property, propertyDescriptor)
property.checkTypeReferences()
modifiersChecker.checkModifiersForDeclaration(property, propertyDescriptor)
identifierChecker.checkDeclaration(property, trace)
}
@@ -93,6 +127,7 @@ class DeclarationsChecker(
}
private fun checkConstructorDeclaration(constructorDescriptor: ConstructorDescriptor, declaration: KtDeclaration) {
declaration.checkTypeReferences()
modifiersChecker.checkModifiersForDeclaration(declaration, constructorDescriptor)
identifierChecker.checkDeclaration(declaration, trace)
}
@@ -113,19 +148,17 @@ class DeclarationsChecker(
private fun checkTypesInClassHeader(classOrObject: KtClassOrObject) {
for (delegationSpecifier in classOrObject.getDelegationSpecifiers()) {
delegationSpecifier.typeReference?.checkBoundsForTypeInClassHeader()
delegationSpecifier.typeReference?.check(checkBoundsForTypeInClassHeader = true)
}
if (classOrObject !is KtClass) return
for (jetTypeParameter in classOrObject.typeParameters) {
jetTypeParameter.extendsBound?.checkBoundsForTypeInClassHeader()
jetTypeParameter.extendsBound?.checkFinalUpperBounds()
jetTypeParameter.extendsBound?.check(checkBoundsForTypeInClassHeader = true, checkFinalUpperBounds = true)
}
for (constraint in classOrObject.typeConstraints) {
constraint.boundTypeReference?.checkBoundsForTypeInClassHeader()
constraint.boundTypeReference?.checkFinalUpperBounds()
constraint.boundTypeReference?.check(checkBoundsForTypeInClassHeader = true, checkFinalUpperBounds = true)
}
}
@@ -137,6 +170,18 @@ class DeclarationsChecker(
trace.bindingContext.get(TYPE, this)?.let { DescriptorResolver.checkUpperBoundType(this, it, trace) }
}
private fun KtTypeReference.check(checkBoundsForTypeInClassHeader: Boolean = false, checkFinalUpperBounds: Boolean = false) {
if (checkFinalUpperBounds) {
checkFinalUpperBounds()
}
else {
checkNotEnumEntry(trace)
}
if (checkBoundsForTypeInClassHeader) {
checkBoundsForTypeInClassHeader()
}
}
private fun checkSupertypesForConsistency(
classifierDescriptor: ClassifierDescriptor,
sourceElement: PsiElement) {
@@ -151,7 +196,7 @@ class DeclarationsChecker(
removeDuplicateTypes(conflictingTypes)
if (conflictingTypes.size > 1) {
val containingDeclaration = typeParameterDescriptor.containingDeclaration as? ClassDescriptor
?: throw AssertionError("Not a class descriptor : " + typeParameterDescriptor.containingDeclaration)
?: throw AssertionError("Not a class descriptor : " + typeParameterDescriptor.containingDeclaration)
if (sourceElement is KtClassOrObject) {
val delegationSpecifierList = sourceElement.getDelegationSpecifierList() ?: continue
trace.report(INCONSISTENT_TYPE_PARAMETER_VALUES.on(delegationSpecifierList,
@@ -593,6 +638,7 @@ class DeclarationsChecker(
for (accessor in property.accessors) {
val propertyAccessorDescriptor = (if (accessor.isGetter) propertyDescriptor.getter else propertyDescriptor.setter)
?: throw AssertionError("No property accessor descriptor for ${property.text}")
accessor.checkTypeReferences()
modifiersChecker.checkModifiersForDeclaration(accessor, propertyAccessorDescriptor)
identifierChecker.checkDeclaration(accessor, trace)
}
@@ -613,7 +659,7 @@ class DeclarationsChecker(
if (accessor == null || accessorDescriptor == null) return
val accessorModifierList = accessor.modifierList ?: return
val tokens = modifiersChecker.getTokensCorrespondingToModifiers(accessorModifierList,
Sets.newHashSet(KtTokens.PUBLIC_KEYWORD, KtTokens.PROTECTED_KEYWORD, KtTokens.PRIVATE_KEYWORD, KtTokens.INTERNAL_KEYWORD))
Sets.newHashSet(KtTokens.PUBLIC_KEYWORD, KtTokens.PROTECTED_KEYWORD, KtTokens.PRIVATE_KEYWORD, KtTokens.INTERNAL_KEYWORD))
if (accessor.isGetter) {
if (accessorDescriptor.visibility != propertyDescriptor.visibility) {
reportVisibilityModifierDiagnostics(tokens.values, Errors.GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY)
@@ -579,6 +579,7 @@ public class DescriptorResolver {
@NotNull KotlinType upperBoundType,
BindingTrace trace
) {
if (DeclarationsCheckerKt.checkNotEnumEntry(upperBound, trace)) return;
if (!TypeUtils.canHaveSubtypes(KotlinTypeChecker.DEFAULT, upperBoundType)) {
ClassifierDescriptor descriptor = upperBoundType.getConstructor().getDeclarationDescriptor();
if (descriptor instanceof ClassDescriptor) {
@@ -237,6 +237,8 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
) {
if (actualType == null || noExpectedType(targetType) || targetType.isError()) return;
DeclarationsCheckerKt.checkNotEnumEntry(expression.getRight(), context.trace);
if (DynamicTypesKt.isDynamic(targetType)) {
KtTypeReference right = expression.getRight();
assert right != null : "We know target is dynamic, but RHS is missing";
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.DeclarationsCheckerKt;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace;
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache;
@@ -155,6 +156,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
ExpressionTypingUtils.checkVariableShadowing(context.scope, context.trace, propertyDescriptor);
scope.addVariableDescriptor(propertyDescriptor);
DeclarationsCheckerKt.checkTypeReferences(property, context.trace);
components.modifiersChecker.withTrace(context.trace).checkModifiersForLocalDeclaration(property, propertyDescriptor);
components.identifierChecker.checkDeclaration(property, context.trace);
return typeInfo.replaceType(components.dataFlowAnalyzer.checkStatementType(property, context));
@@ -100,6 +100,7 @@ internal class FunctionsTypingVisitor(facade: ExpressionTypingInternals) : Expre
function.getValueParameters(), functionDescriptor.getValueParameters(), context.scope, context.dataFlowInfo, context.trace
)
function.checkTypeReferences(context.trace)
components.modifiersChecker.withTrace(context.trace).checkModifiersForLocalDeclaration(function, functionDescriptor)
components.identifierChecker.checkDeclaration(function, context.trace)
if (!function.hasBody() && !function.hasModifier(KtTokens.EXTERNAL_KEYWORD)) {
+34
View File
@@ -0,0 +1,34 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
enum class Color { RED }
class MyColor(val x: <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>, y: <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>) : <!ENUM_ENTRY_AS_TYPE!>Color.RED<!> {
var z: <!ENUM_ENTRY_AS_TYPE!>Color.RED<!> = <!TYPE_MISMATCH!>Color.RED<!>
set(arg: <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>) { z = arg }
fun foo(arg: <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>): <!ENUM_ENTRY_AS_TYPE!>Color.RED<!> = arg
fun bar(): <!ENUM_ENTRY_AS_TYPE!>Color.RED<!> {
class Local : <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>
fun local(arg: <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>): <!ENUM_ENTRY_AS_TYPE!>Color.RED<!> = arg
val temp: <!ENUM_ENTRY_AS_TYPE!>Color.RED<!> = <!TYPE_MISMATCH!>Color.RED<!>
temp <!USELESS_CAST!>as? <!ENUM_ENTRY_AS_TYPE!>Color.RED<!><!>
if (temp is <!IS_ENUM_ENTRY!>Color.RED<!>) {
return temp <!USELESS_CAST!>as <!ENUM_ENTRY_AS_TYPE!>Color.RED<!><!>
}
val obj = object : <!ENUM_ENTRY_AS_TYPE!>Color.RED<!> {}
if (obj is <!IS_ENUM_ENTRY!>Color.RED<!>) {
return obj
}
return <!TYPE_MISMATCH!>Color.RED<!>
}
}
fun create(): Array<<!ENUM_ENTRY_AS_TYPE!>Color.RED<!>>? = null
interface Your<T : <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>>
class His : Your<<!ENUM_ENTRY_AS_TYPE!>Color.RED<!>>
fun <T : <!ENUM_ENTRY_AS_TYPE!>Color.RED<!>> otherCreate(): Array<T>? = null
+50
View File
@@ -0,0 +1,50 @@
package
public fun create(): kotlin.Array<Color.RED>?
public fun </*0*/ T : Color.RED> otherCreate(): kotlin.Array<T>?
public final enum class Color : kotlin.Enum<Color> {
enum entry RED
private constructor Color()
public final override /*1*/ /*fake_override*/ val name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: Color): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
// Static members
@kotlin.Deprecated(message = "Use 'values()' function instead", replaceWith = kotlin.ReplaceWith(expression = "this.values()", imports = {})) public final /*synthesized*/ val values: kotlin.Array<Color>
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): Color
public final /*synthesized*/ fun values(): kotlin.Array<Color>
}
public final class His : Your<Color.RED> {
public constructor His()
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 MyColor : Color.RED {
public constructor MyColor(/*0*/ x: Color.RED, /*1*/ y: Color.RED)
public final override /*1*/ /*fake_override*/ val name: kotlin.String
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
public final val x: Color.RED
public final var z: Color.RED
public final fun bar(): Color.RED
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: Color): kotlin.Int
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public final fun foo(/*0*/ arg: Color.RED): Color.RED
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface Your</*0*/ T : Color.RED> {
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
}
@@ -2,4 +2,4 @@ enum class E {
ENTRY
}
class A : <!SINGLETON_IN_SUPERTYPE!>E.ENTRY<!>
class A : <!ENUM_ENTRY_AS_TYPE!>E.ENTRY<!>
@@ -235,6 +235,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("EnumEntryAsType.kt")
public void testEnumEntryAsType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/EnumEntryAsType.kt");
doTest(fileName);
}
@TestMetadata("fileDependencyRecursion.kt")
public void testFileDependencyRecursion() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/fileDependencyRecursion.kt");