Report error when LHS of class literal is not a class

This commit is contained in:
Alexander Udalov
2015-03-08 14:22:30 +03:00
parent b93b9bd565
commit 6468515d99
10 changed files with 183 additions and 27 deletions
@@ -409,6 +409,8 @@ public interface Errors {
DiagnosticFactory1<JetExpression, CallableMemberDescriptor> EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<JetExpression> CALLABLE_REFERENCE_LHS_NOT_A_CLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<JetExpression> CLASS_LITERAL_LHS_NOT_A_CLASS = DiagnosticFactory0.create(ERROR);
// Multi-declarations
DiagnosticFactory0<JetMultiDeclaration> INITIALIZER_REQUIRED_FOR_MULTIDECLARATION = DiagnosticFactory0.create(ERROR, DEFAULT);
@@ -594,7 +594,9 @@ public class DefaultErrorMessages {
MAP.put(EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED,
"''{0}'' is a member and an extension at the same time. References to such elements are not allowed", NAME);
MAP.put(CALLABLE_REFERENCE_LHS_NOT_A_CLASS, "Callable reference left-hand side cannot be a type parameter");
MAP.put(CALLABLE_REFERENCE_LHS_NOT_A_CLASS, "Left hand side of a callable reference cannot be a type parameter");
MAP.put(CLASS_LITERAL_LHS_NOT_A_CLASS, "Only classes are allowed on the left hand side of a class literal");
//Inline
MAP.put(INVISIBLE_MEMBER_FROM_INLINE, "Cannot access effectively non-public-api ''{0}'' member from effectively public-api ''{1}''", SHORT_NAMES_IN_TYPES, SHORT_NAMES_IN_TYPES);
@@ -453,38 +453,72 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
}
@Override
public JetTypeInfo visitClassLiteralExpression(@NotNull JetClassLiteralExpression expression, ExpressionTypingContext context) {
JetType receiverType = resolveDoubleColonExpressionType(expression, context);
// TODO: forbid type parameters & generic classes
ClassifierDescriptor classifier = receiverType.getConstructor().getDeclarationDescriptor();
return JetTypeInfo.create(
components.reflectionTypes.getKClassType(Annotations.EMPTY, (ClassDescriptor) classifier),
context.dataFlowInfo
);
}
public JetTypeInfo visitClassLiteralExpression(@NotNull JetClassLiteralExpression expression, ExpressionTypingContext c) {
ClassDescriptor descriptor = resolveClassLiteral(expression, c);
JetType type = descriptor == null
? ErrorUtils.createErrorType("Unresolved class")
: components.reflectionTypes.getKClassType(Annotations.EMPTY, descriptor);
@Override
public JetTypeInfo visitCallableReferenceExpression(@NotNull JetCallableReferenceExpression expression, ExpressionTypingContext context) {
JetType receiverType = resolveDoubleColonExpressionType(expression, context);
JetSimpleNameExpression callableReference = expression.getCallableReference();
if (callableReference.getReferencedName().isEmpty()) {
context.trace.report(UNRESOLVED_REFERENCE.on(callableReference, callableReference));
JetType errorType = ErrorUtils.createErrorType("Empty callable reference");
return DataFlowUtils.checkType(errorType, expression, context, context.dataFlowInfo);
}
JetType result = getCallableReferenceType(expression, receiverType, context);
return DataFlowUtils.checkType(result, expression, context, context.dataFlowInfo);
return JetTypeInfo.create(type, c.dataFlowInfo);
}
@Nullable
private JetType resolveDoubleColonExpressionType(@NotNull JetDoubleColonExpression expression, ExpressionTypingContext context) {
private ClassDescriptor resolveClassLiteral(@NotNull JetClassLiteralExpression expression, ExpressionTypingContext c) {
JetTypeReference typeReference = expression.getTypeReference();
return typeReference == null
? null
: components.expressionTypingServices.getTypeResolver().resolveType(context.scope, typeReference, context.trace, false);
if (typeReference == null) {
// "::class" will mean "this::class", a class of "this" instance
c.trace.report(UNSUPPORTED.on(expression, "Class literals with empty left hand side are not yet supported"));
return null;
}
TypeResolutionContext context =
new TypeResolutionContext(c.scope, c.trace, /* checkBounds = */ false, /* allowBareTypes = */ true);
PossiblyBareType possiblyBareType =
components.expressionTypingServices.getTypeResolver().resolvePossiblyBareType(context, typeReference);
TypeConstructor typeConstructor = null;
if (possiblyBareType.isBare()) {
if (!possiblyBareType.isNullable()) {
typeConstructor = possiblyBareType.getBareTypeConstructor();
}
}
else {
JetType type = possiblyBareType.getActualType();
if (!type.isMarkedNullable() && type.getArguments().isEmpty()) {
typeConstructor = type.getConstructor();
}
}
if (typeConstructor != null) {
ClassifierDescriptor classifier = typeConstructor.getDeclarationDescriptor();
if (classifier instanceof ClassDescriptor) {
return (ClassDescriptor) classifier;
}
}
context.trace.report(CLASS_LITERAL_LHS_NOT_A_CLASS.on(expression));
return null;
}
@Override
public JetTypeInfo visitCallableReferenceExpression(@NotNull JetCallableReferenceExpression expression, ExpressionTypingContext c) {
JetTypeReference typeReference = expression.getTypeReference();
JetType receiverType =
typeReference == null
? null
: components.expressionTypingServices.getTypeResolver().resolveType(c.scope, typeReference, c.trace, false);
JetSimpleNameExpression callableReference = expression.getCallableReference();
if (callableReference.getReferencedName().isEmpty()) {
c.trace.report(UNRESOLVED_REFERENCE.on(callableReference, callableReference));
JetType errorType = ErrorUtils.createErrorType("Empty callable reference");
return DataFlowUtils.checkType(errorType, expression, c, c.dataFlowInfo);
}
JetType result = getCallableReferenceType(expression, receiverType, c);
return DataFlowUtils.checkType(result, expression, c, c.dataFlowInfo);
}
@Nullable
@@ -0,0 +1,9 @@
import kotlin.test.assertEquals
class Generic<K, V>
fun box(): String {
val g = Generic::class
assertEquals("Generic", g.simpleName)
return "OK"
}
@@ -0,0 +1,26 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
class A<T> {
class Nested<N>
inner class Inner<I>
}
val a1 = A::class
val a2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A<*>::class<!>
val a3 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A<String>::class<!>
val a4 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A<out String?>::class<!>
val n1 = A.Nested::class
val n2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A.Nested<*>::class<!>
val i1 = A.Inner::class
val i2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A<*>.Inner<*>::class<!>
val i3 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A<<!DEBUG_INFO_MISSING_UNRESOLVED!>Int<!>>.Inner<CharSequence>::class<!>
val m1 = Map::class
val m2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Map<Int, *>::class<!>
val m3 = Map.Entry::class
val b1 = Int::class
val b2 = Nothing::class
@@ -0,0 +1,37 @@
package
internal val a1: kotlin.reflect.KClass<A<*>>
internal val a2: [ERROR : Unresolved class]
internal val a3: [ERROR : Unresolved class]
internal val a4: [ERROR : Unresolved class]
internal val b1: kotlin.reflect.KClass<kotlin.Int>
internal val b2: kotlin.reflect.KClass<kotlin.Nothing>
internal val i1: kotlin.reflect.KClass<A.Inner<*>>
internal val i2: [ERROR : Unresolved class]
internal val i3: [ERROR : Unresolved class]
internal val m1: kotlin.reflect.KClass<kotlin.Map<*, *>>
internal val m2: [ERROR : Unresolved class]
internal val m3: kotlin.reflect.KClass<kotlin.Map.Entry<*, *>>
internal val n1: kotlin.reflect.KClass<A.Nested<*>>
internal val n2: [ERROR : Unresolved class]
internal final class A</*0*/ T> {
public constructor A</*0*/ T>()
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
internal final inner class Inner</*0*/ I> {
public constructor Inner</*0*/ I>()
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
}
internal final class Nested</*0*/ N> {
public constructor Nested</*0*/ N>()
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
}
}
@@ -0,0 +1,14 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
class A
val a1 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A?::class<!>
val a2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A?<!REDUNDANT_NULLABLE!>?<!>::class<!>
val l1 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>List<String>?::class<!>
val l2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>List?::class<!>
fun foo<T : Any>() {
val t1 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>T::class<!>
val t2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>T?::class<!>
}
@@ -0,0 +1,14 @@
package
internal val a1: [ERROR : Unresolved class]
internal val a2: [ERROR : Unresolved class]
internal val l1: [ERROR : Unresolved class]
internal val l2: [ERROR : Unresolved class]
internal fun </*0*/ T : kotlin.Any> foo(): kotlin.Unit
internal final class A {
public constructor A()
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
}
@@ -607,6 +607,18 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/classLiteral"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("genericClasses.kt")
public void testGenericClasses() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/classLiteral/genericClasses.kt");
doTest(fileName);
}
@TestMetadata("nonClassesOnLHS.kt")
public void testNonClassesOnLHS() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/classLiteral/nonClassesOnLHS.kt");
doTest(fileName);
}
@TestMetadata("simpleClassLiteral.kt")
public void testSimpleClassLiteral() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/classLiteral/simpleClassLiteral.kt");
@@ -2557,6 +2557,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
doTestWithStdlib(fileName);
}
@TestMetadata("genericClass.kt")
public void testGenericClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/classLiterals/genericClass.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("simpleClassLiteral.kt")
public void testSimpleClassLiteral() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/classLiterals/simpleClassLiteral.kt");