Report error when LHS of class literal is not a class
This commit is contained in:
@@ -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);
|
||||
|
||||
+3
-1
@@ -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);
|
||||
|
||||
+60
-26
@@ -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
|
||||
}
|
||||
+12
@@ -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");
|
||||
|
||||
+6
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user