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);
|
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> CALLABLE_REFERENCE_LHS_NOT_A_CLASS = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
|
DiagnosticFactory0<JetExpression> CLASS_LITERAL_LHS_NOT_A_CLASS = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
// Multi-declarations
|
// Multi-declarations
|
||||||
|
|
||||||
DiagnosticFactory0<JetMultiDeclaration> INITIALIZER_REQUIRED_FOR_MULTIDECLARATION = DiagnosticFactory0.create(ERROR, DEFAULT);
|
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,
|
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);
|
"''{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
|
//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);
|
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
|
@Override
|
||||||
public JetTypeInfo visitClassLiteralExpression(@NotNull JetClassLiteralExpression expression, ExpressionTypingContext context) {
|
public JetTypeInfo visitClassLiteralExpression(@NotNull JetClassLiteralExpression expression, ExpressionTypingContext c) {
|
||||||
JetType receiverType = resolveDoubleColonExpressionType(expression, context);
|
ClassDescriptor descriptor = resolveClassLiteral(expression, c);
|
||||||
// TODO: forbid type parameters & generic classes
|
JetType type = descriptor == null
|
||||||
ClassifierDescriptor classifier = receiverType.getConstructor().getDeclarationDescriptor();
|
? ErrorUtils.createErrorType("Unresolved class")
|
||||||
return JetTypeInfo.create(
|
: components.reflectionTypes.getKClassType(Annotations.EMPTY, descriptor);
|
||||||
components.reflectionTypes.getKClassType(Annotations.EMPTY, (ClassDescriptor) classifier),
|
|
||||||
context.dataFlowInfo
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
return JetTypeInfo.create(type, c.dataFlowInfo);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private JetType resolveDoubleColonExpressionType(@NotNull JetDoubleColonExpression expression, ExpressionTypingContext context) {
|
private ClassDescriptor resolveClassLiteral(@NotNull JetClassLiteralExpression expression, ExpressionTypingContext c) {
|
||||||
JetTypeReference typeReference = expression.getTypeReference();
|
JetTypeReference typeReference = expression.getTypeReference();
|
||||||
|
|
||||||
return typeReference == null
|
if (typeReference == null) {
|
||||||
? null
|
// "::class" will mean "this::class", a class of "this" instance
|
||||||
: components.expressionTypingServices.getTypeResolver().resolveType(context.scope, typeReference, context.trace, false);
|
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
|
@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);
|
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")
|
@TestMetadata("simpleClassLiteral.kt")
|
||||||
public void testSimpleClassLiteral() throws Exception {
|
public void testSimpleClassLiteral() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/classLiteral/simpleClassLiteral.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/classLiteral/simpleClassLiteral.kt");
|
||||||
|
|||||||
+6
@@ -2557,6 +2557,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
|
|||||||
doTestWithStdlib(fileName);
|
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")
|
@TestMetadata("simpleClassLiteral.kt")
|
||||||
public void testSimpleClassLiteral() throws Exception {
|
public void testSimpleClassLiteral() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/classLiterals/simpleClassLiteral.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/classLiterals/simpleClassLiteral.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user