FIR checker: report nullable/non-class LHS of class literals
This commit is contained in:
committed by
Dmitriy Novozhilov
parent
a6d1d47918
commit
e8028e7825
+5
@@ -212,6 +212,11 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
|||||||
val INNER_CLASS_OF_GENERIC_THROWABLE_SUBCLASS by error<FirSourceElement, KtClassOrObject>(PositioningStrategy.DECLARATION_NAME)
|
val INNER_CLASS_OF_GENERIC_THROWABLE_SUBCLASS by error<FirSourceElement, KtClassOrObject>(PositioningStrategy.DECLARATION_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val REFLECTION by object : DiagnosticGroup("Reflection") {
|
||||||
|
val CLASS_LITERAL_LHS_NOT_A_CLASS by error<FirSourceElement, KtExpression>()
|
||||||
|
val NULLABLE_TYPE_IN_CLASS_LITERAL_LHS by error<FirSourceElement, KtExpression>()
|
||||||
|
}
|
||||||
|
|
||||||
val OVERRIDES by object : DiagnosticGroup("overrides") {
|
val OVERRIDES by object : DiagnosticGroup("overrides") {
|
||||||
val NOTHING_TO_OVERRIDE by error<FirSourceElement, KtModifierListOwner>(PositioningStrategy.OVERRIDE_MODIFIER) {
|
val NOTHING_TO_OVERRIDE by error<FirSourceElement, KtModifierListOwner>(PositioningStrategy.OVERRIDE_MODIFIER) {
|
||||||
parameter<FirMemberDeclaration>("declaration")
|
parameter<FirMemberDeclaration>("declaration")
|
||||||
|
|||||||
@@ -171,6 +171,10 @@ object FirErrors {
|
|||||||
val GENERIC_THROWABLE_SUBCLASS by error0<FirSourceElement, KtTypeParameterList>()
|
val GENERIC_THROWABLE_SUBCLASS by error0<FirSourceElement, KtTypeParameterList>()
|
||||||
val INNER_CLASS_OF_GENERIC_THROWABLE_SUBCLASS by error0<FirSourceElement, KtClassOrObject>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
val INNER_CLASS_OF_GENERIC_THROWABLE_SUBCLASS by error0<FirSourceElement, KtClassOrObject>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||||
|
|
||||||
|
// Reflection
|
||||||
|
val CLASS_LITERAL_LHS_NOT_A_CLASS by error0<FirSourceElement, KtExpression>()
|
||||||
|
val NULLABLE_TYPE_IN_CLASS_LITERAL_LHS by error0<FirSourceElement, KtExpression>()
|
||||||
|
|
||||||
// overrides
|
// overrides
|
||||||
val NOTHING_TO_OVERRIDE by error1<FirSourceElement, KtModifierListOwner, FirMemberDeclaration>(SourceElementPositioningStrategies.OVERRIDE_MODIFIER)
|
val NOTHING_TO_OVERRIDE by error1<FirSourceElement, KtModifierListOwner, FirMemberDeclaration>(SourceElementPositioningStrategies.OVERRIDE_MODIFIER)
|
||||||
val CANNOT_WEAKEN_ACCESS_PRIVILEGE by error3<FirSourceElement, KtModifierListOwner, Visibility, FirCallableDeclaration<*>, Name>(SourceElementPositioningStrategies.VISIBILITY_MODIFIER)
|
val CANNOT_WEAKEN_ACCESS_PRIVILEGE by error3<FirSourceElement, KtModifierListOwner, Visibility, FirCallableDeclaration<*>, Name>(SourceElementPositioningStrategies.VISIBILITY_MODIFIER)
|
||||||
|
|||||||
@@ -9,16 +9,14 @@ import com.intellij.psi.tree.IElementType
|
|||||||
import com.intellij.psi.tree.TokenSet
|
import com.intellij.psi.tree.TokenSet
|
||||||
import org.jetbrains.kotlin.fir.*
|
import org.jetbrains.kotlin.fir.*
|
||||||
|
|
||||||
fun FirSourceElement.getChild(type: IElementType, index: Int = 0, depth: Int = -1): FirSourceElement? {
|
fun FirSourceElement.getChild(type: IElementType, index: Int = 0, depth: Int = -1): FirSourceElement? =
|
||||||
return getChild(setOf(type), index, depth)
|
getChild(setOf(type), index, depth)
|
||||||
}
|
|
||||||
|
|
||||||
fun FirSourceElement.getChild(types: TokenSet, index: Int = 0, depth: Int = -1): FirSourceElement? {
|
fun FirSourceElement.getChild(types: TokenSet, index: Int = 0, depth: Int = -1): FirSourceElement? =
|
||||||
return getChild(types.types.toSet(), index, depth)
|
getChild(types.types.toSet(), index, depth)
|
||||||
}
|
|
||||||
|
|
||||||
fun FirSourceElement.getChild(types: Set<IElementType>, index: Int = 0, depth: Int = -1): FirSourceElement? {
|
fun FirSourceElement.getChild(types: Set<IElementType>, index: Int = 0, depth: Int = -1): FirSourceElement? =
|
||||||
return when (this) {
|
when (this) {
|
||||||
is FirPsiSourceElement<*> -> {
|
is FirPsiSourceElement<*> -> {
|
||||||
getChild(types, index, depth)
|
getChild(types, index, depth)
|
||||||
}
|
}
|
||||||
@@ -27,7 +25,6 @@ fun FirSourceElement.getChild(types: Set<IElementType>, index: Int = 0, depth: I
|
|||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun FirPsiSourceElement<*>.getChild(types: Set<IElementType>, index: Int, depth: Int): FirSourceElement? {
|
private fun FirPsiSourceElement<*>.getChild(types: Set<IElementType>, index: Int, depth: Int): FirSourceElement? {
|
||||||
val visitor = PsiElementFinderByType(types, index, depth)
|
val visitor = PsiElementFinderByType(types, index, depth)
|
||||||
@@ -36,6 +33,5 @@ private fun FirPsiSourceElement<*>.getChild(types: Set<IElementType>, index: Int
|
|||||||
|
|
||||||
private fun FirLightSourceElement.getChild(types: Set<IElementType>, index: Int, depth: Int): FirSourceElement? {
|
private fun FirLightSourceElement.getChild(types: Set<IElementType>, index: Int, depth: Int): FirSourceElement? {
|
||||||
val visitor = LighterTreeElementFinderByType(treeStructure, types, index, depth)
|
val visitor = LighterTreeElementFinderByType(treeStructure, types, index, depth)
|
||||||
|
|
||||||
return visitor.find(lighterASTNode)?.toFirLightSourceElement(treeStructure)
|
return visitor.find(lighterASTNode)?.toFirLightSourceElement(treeStructure)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -21,7 +21,7 @@ class LighterTreeElementFinderByType(
|
|||||||
return visitNode(node, 0)
|
return visitNode(node, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visitNode(node: LighterASTNode, currentDepth: Int): LighterASTNode? {
|
private fun visitNode(node: LighterASTNode, currentDepth: Int): LighterASTNode? {
|
||||||
if (node.tokenType in types) {
|
if (node.tokenType in types) {
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
return node
|
return node
|
||||||
@@ -44,4 +44,5 @@ class LighterTreeElementFinderByType(
|
|||||||
tree.getChildren(this, ref)
|
tree.getChildren(this, ref)
|
||||||
return ref.get()?.filterNotNull() ?: emptyList()
|
return ref.get()?.filterNotNull() ?: emptyList()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|||||||
+5
-4
@@ -18,7 +18,7 @@ class PsiElementFinderByType(
|
|||||||
return visitElement(root, 0)
|
return visitElement(root, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visitElement(element: PsiElement, currentDepth: Int): PsiElement? {
|
private fun visitElement(element: PsiElement, currentDepth: Int): PsiElement? {
|
||||||
if (element.node.elementType in types) {
|
if (element.node.elementType in types) {
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
return element
|
return element
|
||||||
@@ -28,11 +28,12 @@ class PsiElementFinderByType(
|
|||||||
|
|
||||||
if (currentDepth == depth) return null
|
if (currentDepth == depth) return null
|
||||||
|
|
||||||
for (children in element.allChildren) {
|
for (child in element.allChildren) {
|
||||||
val result = visitElement(children, currentDepth + 1)
|
val result = visitElement(child, currentDepth + 1)
|
||||||
if (result != null) return result
|
if (result != null) return result
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.getChild
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRefsOwner
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirGetClassCall
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||||
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens.QUEST
|
||||||
|
|
||||||
|
object FirGetClassCallChecker : FirBasicExpressionChecker() {
|
||||||
|
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
|
if (expression !is FirGetClassCall) return
|
||||||
|
val source = expression.source ?: return
|
||||||
|
if (source.kind is FirFakeSourceElementKind) return
|
||||||
|
|
||||||
|
val argument = expression.argument as? FirResolvedQualifier ?: return
|
||||||
|
// Note that raw FIR drops marked nullability "?" in, e.g., `A?::class`, `A<T?>::class`, or `A<T?>?::class`.
|
||||||
|
// That is, AST structures for those expressions have token type QUEST, whereas FIR element doesn't have any information about it.
|
||||||
|
//
|
||||||
|
// A?::class -> CLASS_LITERAL_EXPRESSION(REFERENCE_EXPRESSION QUEST COLONCOLON "class")
|
||||||
|
// A<T?>::class -> CLASS_LITERAL_EXPRESSION(REFERENCE_EXPRESSION TYPE_ARGUMENT_LIST COLONCOLON "class")
|
||||||
|
// A<T?>?::class -> CLASS_LITERAL_EXPRESSION(REFERENCE_EXPRESSION TYPE_ARGUMENT_LIST QUEST COLONCOLON "class")
|
||||||
|
// where TYPE_ARGUMENT_LIST may have QUEST in it
|
||||||
|
//
|
||||||
|
// Only the 2nd example is valid, and we want to check if token type QUEST doesn't exist at the same level as COLONCOLON.
|
||||||
|
val markedNullable = source.getChild(QUEST, depth = 1) != null
|
||||||
|
if (argument.isNullableLHSForCallableReference || markedNullable) {
|
||||||
|
reporter.reportOn(source, FirErrors.NULLABLE_TYPE_IN_CLASS_LITERAL_LHS, context)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: differentiate RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS
|
||||||
|
if (argument.typeArguments.isNotEmpty() && !argument.typeRef.coneType.isAllowedInClassLiteral(context)) {
|
||||||
|
val typeParameters = (argument.symbol?.fir as? FirTypeParameterRefsOwner)?.typeParameters
|
||||||
|
// Among type parameter references, only count actual type parameter while discarding [FirOuterClassTypeParameterRef]
|
||||||
|
val expectedTypeArgumentSize = typeParameters?.filterIsInstance<FirTypeParameter>()?.size ?: 0
|
||||||
|
if (expectedTypeArgumentSize != argument.typeArguments.size) {
|
||||||
|
// Will be reported as WRONG_NUMBER_OF_TYPE_ARGUMENTS
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reporter.reportOn(source, FirErrors.CLASS_LITERAL_LHS_NOT_A_CLASS, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ConeKotlinType.isAllowedInClassLiteral(context: CheckerContext): Boolean =
|
||||||
|
when (this) {
|
||||||
|
is ConeClassLikeType -> {
|
||||||
|
if (isNonPrimitiveArray) {
|
||||||
|
typeArguments.none { typeArgument ->
|
||||||
|
when (typeArgument) {
|
||||||
|
is ConeStarProjection -> true
|
||||||
|
is ConeKotlinTypeProjection -> !typeArgument.type.isAllowedInClassLiteral(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
typeArguments.isEmpty()
|
||||||
|
}
|
||||||
|
is ConeTypeParameterType -> this.lookupTag.typeParameterSymbol.fir.isReified
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CAN_BE_REPLACED_W
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CAN_BE_VAL
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CAN_BE_VAL
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CATCH_PARAMETER_WITH_DEFAULT_VALUE
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CATCH_PARAMETER_WITH_DEFAULT_VALUE
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CLASS_IN_SUPERTYPE_FOR_ENUM
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CLASS_IN_SUPERTYPE_FOR_ENUM
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CLASS_LITERAL_LHS_NOT_A_CLASS
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTION_AMBIGUITY
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTION_AMBIGUITY
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTION_MISSING
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTION_MISSING
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTION_ON_NULLABLE
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.COMPONENT_FUNCTION_ON_NULLABLE
|
||||||
@@ -114,6 +115,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NOT_A_SUPERTYPE
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ELSE_IN_WHEN
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_ELSE_IN_WHEN
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_THIS
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_THIS
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_TYPE_FOR_TYPE_PARAMETER
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NO_TYPE_FOR_TYPE_PARAMETER
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_TYPE_IN_CLASS_LITERAL_LHS
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_TYPE_OF_ANNOTATION_MEMBER
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULLABLE_TYPE_OF_ANNOTATION_MEMBER
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OTHER_ERROR
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OTHER_ERROR
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OVERRIDING_FINAL_MEMBER
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OVERRIDING_FINAL_MEMBER
|
||||||
@@ -360,6 +362,10 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
|||||||
map.put(REIFIED_TYPE_IN_CATCH_CLAUSE, "Reified type is forbidden for catch parameter")
|
map.put(REIFIED_TYPE_IN_CATCH_CLAUSE, "Reified type is forbidden for catch parameter")
|
||||||
map.put(TYPE_PARAMETER_IN_CATCH_CLAUSE, "Type parameter is forbidden for catch parameter")
|
map.put(TYPE_PARAMETER_IN_CATCH_CLAUSE, "Type parameter is forbidden for catch parameter")
|
||||||
|
|
||||||
|
// Reflection
|
||||||
|
map.put(CLASS_LITERAL_LHS_NOT_A_CLASS, "Only classes are allowed on the left hand side of a class literal")
|
||||||
|
map.put(NULLABLE_TYPE_IN_CLASS_LITERAL_LHS, "Type in a class literal must not be nullable")
|
||||||
|
|
||||||
// Overrides
|
// Overrides
|
||||||
map.put(NOTHING_TO_OVERRIDE, "''{0}'' overrides nothing", DECLARATION_NAME)
|
map.put(NOTHING_TO_OVERRIDE, "''{0}'' overrides nothing", DECLARATION_NAME)
|
||||||
map.put(OVERRIDING_FINAL_MEMBER, "''{0}'' in ''{1}'' is final and cannot be overridden", NAME, TO_STRING)
|
map.put(OVERRIDING_FINAL_MEMBER, "''{0}'' in ''{1}'' is final and cannot be overridden", NAME, TO_STRING)
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ val ConeKotlinType.isArrayType: Boolean
|
|||||||
return isBuiltinType(StandardClassIds.Array, false) ||
|
return isBuiltinType(StandardClassIds.Array, false) ||
|
||||||
StandardClassIds.primitiveArrayTypeByElementType.values.any { isBuiltinType(it, false) }
|
StandardClassIds.primitiveArrayTypeByElementType.values.any { isBuiltinType(it, false) }
|
||||||
}
|
}
|
||||||
|
// Same as [KotlinBuiltIns#isNonPrimitiveArray]
|
||||||
|
val ConeKotlinType.isNonPrimitiveArray: Boolean
|
||||||
|
get() = this is ConeClassLikeType && lookupTag.classId == StandardClassIds.Array
|
||||||
|
|
||||||
private val builtinUnsignedTypes = setOf(StandardClassIds.UInt, StandardClassIds.UByte, StandardClassIds.ULong, StandardClassIds.UShort)
|
private val builtinUnsignedTypes = setOf(StandardClassIds.UInt, StandardClassIds.UByte, StandardClassIds.ULong, StandardClassIds.UShort)
|
||||||
val ConeKotlinType.isUnsignedTypeOrNullableUnsignedType: Boolean get() = isAnyOfBuiltinType(builtinUnsignedTypes)
|
val ConeKotlinType.isUnsignedTypeOrNullableUnsignedType: Boolean get() = isAnyOfBuiltinType(builtinUnsignedTypes)
|
||||||
|
|
||||||
|
|||||||
+1
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.analysis.checkers.expression.*
|
|||||||
object CommonExpressionCheckers : ExpressionCheckers() {
|
object CommonExpressionCheckers : ExpressionCheckers() {
|
||||||
override val basicExpressionCheckers: Set<FirBasicExpressionChecker> = setOf(
|
override val basicExpressionCheckers: Set<FirBasicExpressionChecker> = setOf(
|
||||||
FirAnonymousFunctionChecker,
|
FirAnonymousFunctionChecker,
|
||||||
|
FirGetClassCallChecker,
|
||||||
)
|
)
|
||||||
|
|
||||||
override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker> = setOf(
|
override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker> = setOf(
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
fun <T> f1(): KClass<Array<T>> = Array<T>::class
|
fun <T> f1(): KClass<Array<T>> = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Array<T>::class<!>
|
||||||
fun <T> f2(): KClass<Array<Array<T>>> = Array<Array<T>>::class
|
fun <T> f2(): KClass<Array<Array<T>>> = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Array<Array<T>>::class<!>
|
||||||
inline fun <reified T> f3() = Array<T>::class
|
inline fun <reified T> f3() = Array<T>::class
|
||||||
inline fun <reified T> f4() = Array<Array<T>>::class
|
inline fun <reified T> f4() = Array<Array<T>>::class
|
||||||
fun f5(): KClass<Array<Any>> = Array<*>::class
|
fun f5(): KClass<Array<Any>> = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Array<*>::class<!>
|
||||||
fun f6(): KClass<Array<Int?>> = Array<Int?>::class
|
fun f6(): KClass<Array<Int?>> = Array<Int?>::class
|
||||||
fun f7() = Array<List<String>>::class
|
fun f7() = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Array<List<String>>::class<!>
|
||||||
fun f8() = Array<List<String>?>::class
|
fun f8() = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Array<List<String>?>::class<!>
|
||||||
fun f9() = Array<List<*>?>::class
|
fun f9() = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Array<List<*>?>::class<!>
|
||||||
|
|||||||
@@ -7,20 +7,20 @@ class A<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val a1 = A::class
|
val a1 = A::class
|
||||||
val a2 = A<*>::class
|
val a2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A<*>::class<!>
|
||||||
val a3 = A<String>::class
|
val a3 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A<String>::class<!>
|
||||||
val a4 = A<out String?>::class
|
val a4 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A<out String?>::class<!>
|
||||||
|
|
||||||
val n1 = A.Nested::class
|
val n1 = A.Nested::class
|
||||||
val n2 = A.Nested<*>::class
|
val n2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A.Nested<*>::class<!>
|
||||||
|
|
||||||
val i1 = A.Inner::class
|
val i1 = A.Inner::class
|
||||||
val i2 = A<*>.Inner<*>::class
|
val i2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A<*>.Inner<*>::class<!>
|
||||||
val i3 = A<Int>.Inner<CharSequence>::class
|
val i3 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>A<Int>.Inner<CharSequence>::class<!>
|
||||||
|
|
||||||
val m1 = Map::class
|
val m1 = Map::class
|
||||||
val m2 = Map<Int, *>::class
|
val m2 = <!CLASS_LITERAL_LHS_NOT_A_CLASS!>Map<Int, *>::class<!>
|
||||||
val m3 = Map.Entry::class
|
val m3 = Map.Entry::class
|
||||||
|
|
||||||
val b1 = Int::class
|
val b1 = Int::class
|
||||||
val b2 = Nothing::class
|
val b2 = Nothing::class
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
class A
|
class A
|
||||||
|
|
||||||
val a1 = A?::class
|
val a1 = <!NULLABLE_TYPE_IN_CLASS_LITERAL_LHS!>A?::class<!>
|
||||||
val a2 = A??::class
|
val a2 = <!NULLABLE_TYPE_IN_CLASS_LITERAL_LHS!>A??::class<!>
|
||||||
|
|
||||||
val l1 = List<String>?::class
|
val l1 = <!NULLABLE_TYPE_IN_CLASS_LITERAL_LHS!>List<String>?::class<!>
|
||||||
val l2 = List?::class
|
val l2 = <!NULLABLE_TYPE_IN_CLASS_LITERAL_LHS!>List?::class<!>
|
||||||
|
|
||||||
fun <T : Any> foo() {
|
fun <T : Any> foo() {
|
||||||
val t1 = <!OTHER_ERROR!>T<!>::class
|
val t1 = <!OTHER_ERROR!>T<!>::class
|
||||||
|
|||||||
Vendored
+1
-1
@@ -18,7 +18,7 @@ fun test2() {}
|
|||||||
@Foo([Array::class])
|
@Foo([Array::class])
|
||||||
fun test3() {}
|
fun test3() {}
|
||||||
|
|
||||||
@Foo([Gen<Int>::class])
|
@Foo([<!CLASS_LITERAL_LHS_NOT_A_CLASS!>Gen<Int>::class<!>])
|
||||||
fun test4() {}
|
fun test4() {}
|
||||||
|
|
||||||
@Foo([""])
|
@Foo([""])
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ fun <T> test5() = listOf(<!OTHER_ERROR!>T<!>::class)
|
|||||||
|
|
||||||
fun <T> test6(): kotlin.reflect.KClass<T> = <!OTHER_ERROR!>T<!>::class
|
fun <T> test6(): kotlin.reflect.KClass<T> = <!OTHER_ERROR!>T<!>::class
|
||||||
fun <T> test7(): kotlin.reflect.KClass<*> = <!OTHER_ERROR!>T<!>::class
|
fun <T> test7(): kotlin.reflect.KClass<*> = <!OTHER_ERROR!>T<!>::class
|
||||||
fun test8() = String?::class
|
fun test8() = <!NULLABLE_TYPE_IN_CLASS_LITERAL_LHS!>String?::class<!>
|
||||||
|
|
||||||
fun <T> listOf(e: T): List<T> = null!!
|
fun <T> listOf(e: T): List<T> = null!!
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -13,6 +13,6 @@ enum class E {
|
|||||||
val ok4 = E.Entry::class
|
val ok4 = E.Entry::class
|
||||||
|
|
||||||
val fail1 = ""::class
|
val fail1 = ""::class
|
||||||
val fail2 = String?::class
|
val fail2 = <!NULLABLE_TYPE_IN_CLASS_LITERAL_LHS!>String?::class<!>
|
||||||
val fail3 = (C)::class
|
val fail3 = (C)::class
|
||||||
val fail4 = (C.Companion)::class
|
val fail4 = (C.Companion)::class
|
||||||
|
|||||||
+12
@@ -679,6 +679,18 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
|
|||||||
token,
|
token,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
add(FirErrors.CLASS_LITERAL_LHS_NOT_A_CLASS) { firDiagnostic ->
|
||||||
|
ClassLiteralLhsNotAClassImpl(
|
||||||
|
firDiagnostic as FirPsiDiagnostic<*>,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
add(FirErrors.NULLABLE_TYPE_IN_CLASS_LITERAL_LHS) { firDiagnostic ->
|
||||||
|
NullableTypeInClassLiteralLhsImpl(
|
||||||
|
firDiagnostic as FirPsiDiagnostic<*>,
|
||||||
|
token,
|
||||||
|
)
|
||||||
|
}
|
||||||
add(FirErrors.NOTHING_TO_OVERRIDE) { firDiagnostic ->
|
add(FirErrors.NOTHING_TO_OVERRIDE) { firDiagnostic ->
|
||||||
NothingToOverrideImpl(
|
NothingToOverrideImpl(
|
||||||
firSymbolBuilder.buildSymbol(firDiagnostic.a as FirDeclaration),
|
firSymbolBuilder.buildSymbol(firDiagnostic.a as FirDeclaration),
|
||||||
|
|||||||
+8
@@ -483,6 +483,14 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
|
|||||||
override val diagnosticClass get() = InnerClassOfGenericThrowableSubclass::class
|
override val diagnosticClass get() = InnerClassOfGenericThrowableSubclass::class
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class ClassLiteralLhsNotAClass : KtFirDiagnostic<KtExpression>() {
|
||||||
|
override val diagnosticClass get() = ClassLiteralLhsNotAClass::class
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class NullableTypeInClassLiteralLhs : KtFirDiagnostic<KtExpression>() {
|
||||||
|
override val diagnosticClass get() = NullableTypeInClassLiteralLhs::class
|
||||||
|
}
|
||||||
|
|
||||||
abstract class NothingToOverride : KtFirDiagnostic<KtModifierListOwner>() {
|
abstract class NothingToOverride : KtFirDiagnostic<KtModifierListOwner>() {
|
||||||
override val diagnosticClass get() = NothingToOverride::class
|
override val diagnosticClass get() = NothingToOverride::class
|
||||||
abstract val declaration: KtSymbol
|
abstract val declaration: KtSymbol
|
||||||
|
|||||||
+14
@@ -775,6 +775,20 @@ internal class InnerClassOfGenericThrowableSubclassImpl(
|
|||||||
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class ClassLiteralLhsNotAClassImpl(
|
||||||
|
firDiagnostic: FirPsiDiagnostic<*>,
|
||||||
|
override val token: ValidityToken,
|
||||||
|
) : KtFirDiagnostic.ClassLiteralLhsNotAClass(), KtAbstractFirDiagnostic<KtExpression> {
|
||||||
|
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class NullableTypeInClassLiteralLhsImpl(
|
||||||
|
firDiagnostic: FirPsiDiagnostic<*>,
|
||||||
|
override val token: ValidityToken,
|
||||||
|
) : KtFirDiagnostic.NullableTypeInClassLiteralLhs(), KtAbstractFirDiagnostic<KtExpression> {
|
||||||
|
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
internal class NothingToOverrideImpl(
|
internal class NothingToOverrideImpl(
|
||||||
override val declaration: KtSymbol,
|
override val declaration: KtSymbol,
|
||||||
firDiagnostic: FirPsiDiagnostic<*>,
|
firDiagnostic: FirPsiDiagnostic<*>,
|
||||||
|
|||||||
Reference in New Issue
Block a user