FIR checker: report KClass with incorrect type argument
This commit is contained in:
committed by
Mikhail Glukhikh
parent
bc2228d434
commit
7fff4108b6
+4
-1
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.fir.PrivateForInline
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
@@ -247,6 +246,10 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
||||
val GENERIC_THROWABLE_SUBCLASS by error<FirSourceElement, KtTypeParameterList>()
|
||||
val INNER_CLASS_OF_GENERIC_THROWABLE_SUBCLASS by error<FirSourceElement, KtClassOrObject>(PositioningStrategy.DECLARATION_NAME)
|
||||
|
||||
val KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE by error<FirSourceElement, KtNamedDeclaration>(PositioningStrategy.DECLARATION_NAME) {
|
||||
parameter<FirTypeParameterSymbol>("typeParameter")
|
||||
}
|
||||
|
||||
val TYPE_PARAMETER_AS_REIFIED by error<FirSourceElement, PsiElement> {
|
||||
parameter<FirTypeParameterSymbol>("typeParameter")
|
||||
}
|
||||
|
||||
@@ -193,6 +193,7 @@ object FirErrors {
|
||||
val TYPE_PARAMETER_IN_CATCH_CLAUSE by error0<FirSourceElement, PsiElement>()
|
||||
val GENERIC_THROWABLE_SUBCLASS by error0<FirSourceElement, KtTypeParameterList>()
|
||||
val INNER_CLASS_OF_GENERIC_THROWABLE_SUBCLASS by error0<FirSourceElement, KtClassOrObject>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||
val KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE by error1<FirSourceElement, KtNamedDeclaration, FirTypeParameterSymbol>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||
val TYPE_PARAMETER_AS_REIFIED by error1<FirSourceElement, PsiElement, FirTypeParameterSymbol>()
|
||||
|
||||
// Reflection
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.psi.KtModifierList
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.psi.KtParameter.VAL_VAR_TOKEN_SET
|
||||
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
/**
|
||||
@@ -319,6 +320,10 @@ fun Modality.toToken(): KtModifierKeywordToken = when (this) {
|
||||
val FirFunctionCall.isIterator
|
||||
get() = this.calleeReference.name.asString() == "<iterator>"
|
||||
|
||||
fun ConeKotlinType.isSubtypeOfAny(session: FirSession): Boolean {
|
||||
return AbstractTypeChecker.isSubtypeOf(session.typeContext, this.fullyExpandedType(session), session.builtinTypes.anyType.type)
|
||||
}
|
||||
|
||||
internal fun throwableClassLikeType(session: FirSession) = session.builtinTypes.throwableType.type
|
||||
|
||||
fun ConeKotlinType.isSubtypeOfThrowable(session: FirSession) =
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.declaration
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isSubtypeOfAny
|
||||
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.declarations.*
|
||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeTypeParameterInQualifiedAccess
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.isKClassType
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.toConeType
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
|
||||
// FE1.0 [KClassWithIncorrectTypeArgumentChecker]
|
||||
object FirKClassWithIncorrectTypeArgumentChecker : FirFileChecker() {
|
||||
override fun check(declaration: FirFile, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
for (topLevelDeclaration in declaration.declarations) {
|
||||
if (topLevelDeclaration is FirCallableMemberDeclaration<*>) {
|
||||
checkTopLevelDeclaration(topLevelDeclaration, context, reporter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When a type parameter is used as a type argument for KClass, it shouldn't be nullable.
|
||||
// bad: fun <T> test1() = T::class
|
||||
// okay: fun <T: Any> test2() = T::class
|
||||
private fun checkTopLevelDeclaration(
|
||||
declaration: FirCallableMemberDeclaration<*>,
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
// prevent duplicate reporting
|
||||
if (declaration is FirPropertyAccessor) return
|
||||
|
||||
val source = declaration.source ?: return
|
||||
if (source.kind is FirFakeSourceElementKind) return
|
||||
|
||||
val returnType = declaration.returnTypeRef.coneType
|
||||
if (!returnType.isKClassWithBadArgument(context.session)) return
|
||||
|
||||
val typeArgument = (returnType.typeArguments[0] as ConeKotlinTypeProjection).type
|
||||
typeArgument.typeParameterFromError?.let {
|
||||
reporter.reportOn(source, FirErrors.KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE, it.symbol, context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ConeKotlinType.isKClassWithBadArgument(session: FirSession): Boolean {
|
||||
if (!this.isKClassType()) return false
|
||||
val argumentType = typeArguments.toList().singleOrNull()?.let {
|
||||
when (it) {
|
||||
is ConeStarProjection -> null
|
||||
is ConeKotlinTypeProjection -> it.type
|
||||
}
|
||||
} ?: return false
|
||||
argumentType.typeParameterFromError?.let { typeParameter ->
|
||||
return !typeParameter.toConeType().isSubtypeOfAny(session)
|
||||
}
|
||||
return argumentType is ConeKotlinErrorType || !argumentType.isSubtypeOfAny(session)
|
||||
}
|
||||
|
||||
private val ConeKotlinType.typeParameterFromError: FirTypeParameter?
|
||||
get() = ((this as? ConeKotlinErrorType)?.diagnostic as? ConeTypeParameterInQualifiedAccess)?.symbol?.fir
|
||||
|
||||
}
|
||||
+8
@@ -111,6 +111,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INSTANCE_ACCESS_B
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INTERFACE_WITH_SUPERCLASS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_IF_AS_EXPRESSION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_TYPE_OF_ANNOTATION_MEMBER
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LEAKED_IN_PLACE_LAMBDA
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LOCAL_ANNOTATION_CLASS_ERROR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LOCAL_INTERFACE_NOT_ALLOWED
|
||||
@@ -395,6 +396,13 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
|
||||
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(
|
||||
KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE,
|
||||
"Declaration has an inconsistent return type. " +
|
||||
"Please add upper bound Any for type parameter ''{0}'' or specify return type explicitly",
|
||||
SYMBOL
|
||||
)
|
||||
|
||||
map.put(TYPE_PARAMETER_AS_REIFIED, "Cannot use ''{0}'' as reified type parameter. Use a class instead", SYMBOL)
|
||||
|
||||
// Reflection
|
||||
|
||||
+1
@@ -70,6 +70,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
)
|
||||
|
||||
override val fileCheckers: Set<FirFileChecker> = setOf(
|
||||
FirKClassWithIncorrectTypeArgumentChecker,
|
||||
FirTopLevelFunctionsChecker,
|
||||
FirTopLevelPropertiesChecker,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user