[FIR] Improve TYPE_PARAMETER_AS_REIFIED detecting, implement TYPE_PARAMETER_AS_REIFIED_ARRAY, TYPE_PARAMETER_AS_REIFIED_ARRAY_WARNING

This commit is contained in:
Ivan Kochurkin
2021-07-14 23:13:50 +03:00
committed by teamcityserver
parent 2333b1bcf6
commit bade6cb611
26 changed files with 161 additions and 120 deletions
@@ -483,6 +483,14 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<FirTypeParameterSymbol>("typeParameter")
}
val TYPE_PARAMETER_AS_REIFIED_ARRAY by error<PsiElement> {
parameter<FirTypeParameterSymbol>("typeParameter")
}
val TYPE_PARAMETER_AS_REIFIED_ARRAY_WARNING by warning<PsiElement> {
parameter<FirTypeParameterSymbol>("typeParameter")
}
val FINAL_UPPER_BOUND by warning<KtTypeReference> {
parameter<ConeKotlinType>("type")
}
@@ -319,6 +319,8 @@ object FirErrors {
val INNER_CLASS_OF_GENERIC_THROWABLE_SUBCLASS by error0<KtClassOrObject>(SourceElementPositioningStrategies.DECLARATION_NAME)
val KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE by error1<KtNamedDeclaration, FirTypeParameterSymbol>(SourceElementPositioningStrategies.DECLARATION_NAME)
val TYPE_PARAMETER_AS_REIFIED by error1<PsiElement, FirTypeParameterSymbol>()
val TYPE_PARAMETER_AS_REIFIED_ARRAY by error1<PsiElement, FirTypeParameterSymbol>()
val TYPE_PARAMETER_AS_REIFIED_ARRAY_WARNING by warning1<PsiElement, FirTypeParameterSymbol>()
val FINAL_UPPER_BOUND by warning1<KtTypeReference, ConeKotlinType>()
val UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE by error0<KtTypeReference>()
val BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER by error0<KtElement>()
@@ -42,7 +42,8 @@ object CommonExpressionCheckers : ExpressionCheckers() {
FirTypeParameterInQualifiedAccessChecker,
FirSealedClassConstructorCallChecker,
FirUninitializedEnumChecker,
FirFunInterfaceConstructorReferenceChecker
FirFunInterfaceConstructorReferenceChecker,
FirTypeParameterAsReifiedChecker
)
override val functionCallCheckers: Set<FirFunctionCallChecker>
@@ -601,24 +601,20 @@ fun extractArgumentTypeRefAndSource(typeRef: FirTypeRef?, index: Int): FirTypeRe
if (typeRef is FirResolvedTypeRef) {
val delegatedTypeRef = typeRef.delegatedTypeRef
if (delegatedTypeRef is FirUserTypeRef) {
var currentTypeArguments: List<FirTypeProjection>? = null
var currentIndex = index
val qualifier = delegatedTypeRef.qualifier
for (i in qualifier.size - 1 downTo 0) {
val typeArguments = qualifier[i].typeArgumentList.typeArguments
if (currentIndex < typeArguments.size) {
currentTypeArguments = typeArguments
break
val typeArgument = typeArguments.elementAtOrNull(currentIndex)
return if (typeArgument is FirTypeProjection)
FirTypeRefSource((typeArgument as? FirTypeProjectionWithVariance)?.typeRef, typeArgument.source)
else null
} else {
currentIndex -= typeArguments.size
}
}
val typeArgument = currentTypeArguments?.elementAtOrNull(currentIndex)
if (typeArgument is FirTypeProjection) {
return FirTypeRefSource((typeArgument as? FirTypeProjectionWithVariance)?.typeRef, typeArgument.source)
}
} else if (delegatedTypeRef is FirFunctionTypeRef) {
val valueParameters = delegatedTypeRef.valueParameters
if (index < valueParameters.size) {
@@ -0,0 +1,78 @@
/*
* 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.config.LanguageFeature
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticFactory1
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.StandardClassIds
object FirTypeParameterAsReifiedChecker : FirQualifiedAccessExpressionChecker() {
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
val calleReference = expression.calleeReference
val typeArguments = expression.typeArguments
val typeParameters = calleReference.toResolvedCallableSymbol()?.typeParameterSymbols ?: return
val count = minOf(typeArguments.size, typeParameters.size)
for (index in 0 until count) {
val typeArgumentProjection = typeArguments.elementAt(index)
val source = typeArgumentProjection.source ?: calleReference.source
val typeArgument = typeArgumentProjection.toConeTypeProjection().type
val typeParameter = typeParameters[index]
if (source != null && (typeParameter.isTypeParameterOfKotlinArray())) {
checkArgumentAndReport(typeArgument, source, false, context, reporter)
}
}
}
private fun FirTypeParameterSymbol.isTypeParameterOfKotlinArray(): Boolean {
val containingDeclaration = containingDeclarationSymbol
return isReified ||
containingDeclaration is FirRegularClassSymbol && containingDeclaration.classId == StandardClassIds.Array
}
private fun checkArgumentAndReport(
typeArgument: ConeKotlinType?,
source: FirSourceElement,
isArray: Boolean,
context: CheckerContext,
reporter: DiagnosticReporter
) {
if (typeArgument?.classId == StandardClassIds.Array) {
checkArgumentAndReport(typeArgument.typeArguments[0].type, source, true, context, reporter)
return
}
var factory: FirDiagnosticFactory1<FirTypeParameterSymbol>? = null
lateinit var symbol: FirTypeParameterSymbol
if (typeArgument is ConeTypeParameterType) {
factory = if (isArray) {
if (context.session.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitNonReifiedArraysAsReifiedTypeArguments))
FirErrors.TYPE_PARAMETER_AS_REIFIED_ARRAY else
FirErrors.TYPE_PARAMETER_AS_REIFIED_ARRAY_WARNING
} else {
FirErrors.TYPE_PARAMETER_AS_REIFIED
}
symbol = typeArgument.toSymbol(context.session) as FirTypeParameterSymbol
}
if (factory != null && !symbol.isReified) {
reporter.reportOn(source, factory, symbol, context)
}
}
}
@@ -41,6 +41,10 @@ class ExpressionCheckersDiagnosticComponent(
checkers.allFunctionCallCheckers.check(functionCall, data, reporter)
}
override fun visitImplicitInvokeCall(implicitInvokeCall: FirImplicitInvokeCall, data: CheckerContext) {
checkers.allFunctionCallCheckers.check(implicitInvokeCall, data, reporter)
}
override fun visitCallableReferenceAccess(callableReferenceAccess: FirCallableReferenceAccess, data: CheckerContext) {
checkers.allCallableReferenceAccessCheckers.check(callableReferenceAccess, data, reporter)
}
@@ -365,6 +365,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETERS_I
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETERS_IN_OBJECT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETERS_NOT_ALLOWED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_AS_REIFIED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_AS_REIFIED_ARRAY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_AS_REIFIED_ARRAY_WARNING
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_IN_CATCH_CLAUSE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_IS_NOT_AN_EXPRESSION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_PARAMETER_OF_PROPERTY_NOT_USED_IN_RECEIVER
@@ -782,6 +784,16 @@ class FirDefaultErrorMessages {
)
map.put(TYPE_PARAMETER_AS_REIFIED, "Cannot use ''{0}'' as reified type parameter. Use a class instead", SYMBOL)
map.put(
TYPE_PARAMETER_AS_REIFIED_ARRAY,
"Cannot use ''{0}'' as reified type parameter, since the array type parameter is not reified.",
SYMBOL
)
map.put(
TYPE_PARAMETER_AS_REIFIED_ARRAY_WARNING,
"Cannot use ''{0}'' as reified type parameter, since the array type parameter is not reified.",
SYMBOL
)
map.put(
FINAL_UPPER_BOUND,
"''{0}'' is a final type, and thus a value of the type parameter is predetermined",
@@ -1,13 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
inline operator fun <reified T> T.plus(p: T): T = this
inline operator fun <reified T> T.invoke(): T = this
fun <A> main(tp: A, any: Any) {
tp + tp
any + any
tp()
any()
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
inline operator fun <reified T> T.plus(p: T): T = this
@@ -1,19 +0,0 @@
// !LANGUAGE: +ProhibitNonReifiedArraysAsReifiedTypeArguments
// !DIAGNOSTICS: -UNUSED_PARAMETER
inline fun <reified T> foo() {}
fun <T> bar() {
foo<T>()
foo<Array<T>>()
foo<Array<Array<T>>>()
foo<Array<Int>>()
foo<Array<Array<Int>>>()
foo<IntArray>()
foo<List<T>>()
foo<List<Array<T>>>()
}
fun test(x: Array<String>, y: Array<*>) {
bar<Int>()
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !LANGUAGE: +ProhibitNonReifiedArraysAsReifiedTypeArguments
// !DIAGNOSTICS: -UNUSED_PARAMETER
@@ -1,19 +0,0 @@
// !LANGUAGE: -ProhibitNonReifiedArraysAsReifiedTypeArguments
// !DIAGNOSTICS: -UNUSED_PARAMETER
inline fun <reified T> foo() {}
fun <T> bar() {
foo<T>()
foo<Array<T>>()
foo<Array<Array<T>>>()
foo<Array<Int>>()
foo<Array<Array<Int>>>()
foo<IntArray>()
foo<List<T>>()
foo<List<Array<T>>>()
}
fun test(x: Array<String>, y: Array<*>) {
bar<Int>()
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !LANGUAGE: -ProhibitNonReifiedArraysAsReifiedTypeArguments
// !DIAGNOSTICS: -UNUSED_PARAMETER
@@ -1,16 +0,0 @@
class C<<!REIFIED_TYPE_PARAMETER_NO_INLINE!>reified<!> T>
fun <T> id(p: T): T = p
fun <A> main() {
<!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>C<!>()
val a: C<A> = C()
C<A>()
val b: C<Int> = C()
C<Int>()
// TODO svtk, uncomment when extensions are called for nested calls!
//val < !UNUSED_VARIABLE!>с< !>: C<A> = id(< !TYPE_PARAMETER_AS_REIFIED!>C< !>())
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
class C<<!REIFIED_TYPE_PARAMETER_NO_INLINE!>reified<!> T>
fun <T> id(p: T): T = p
@@ -11,11 +11,11 @@ fun <T> id(p: T): T = p
fun <A> main() {
<!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>f<!>()
val a: A = f()
f<A>()
val a: A = <!TYPE_PARAMETER_AS_REIFIED!>f<!>()
f<<!TYPE_PARAMETER_AS_REIFIED!>A<!>>()
val b: Int = f()
f<Int>()
val с: A = id(f())
val с: A = id(<!TYPE_PARAMETER_AS_REIFIED!>f<!>())
}
@@ -50,6 +50,6 @@ fun test6() = foo<Nothing>()
class B<T>(val array: Array<T>)
fun <T> bar() = B<Array<T>>(arrayOf())
fun <T> bar() = B<Array<T>>(<!TYPE_PARAMETER_AS_REIFIED_ARRAY!>arrayOf<!>())
fun test7() = bar<Nothing>()
@@ -1,17 +0,0 @@
fun <T> fail1(): Array<T> = Array(1) { null!! }
fun <T> ok1(block: () -> Array<T>): Array<T> = block()
inline fun <reified T> ok2(): Array<T> = Array(1) { null!! }
fun <T> fail2(): Array<T> = ok1 { Array<T>(1) { null!! } }
fun <T> ok3(block: () -> Array<T>): Array<T> = ok1 { block() }
inline fun <reified T> ok4(): Array<T> = ok1 { Array<T>(1) { null!! } }
fun <T> fail3(block: () -> T): Pair<Array<T>, Array<T>> = Pair(arrayOf(
block()), arrayOf()
)
inline fun <reified T> ok5(block: () -> T): Pair<Array<T>, Array<T>> = Pair(
arrayOf(block()), arrayOf()
)
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
fun <T> fail1(): Array<T> = <!TYPE_PARAMETER_AS_REIFIED!>Array<!>(1) { null!! }
fun <T> ok1(block: () -> Array<T>): Array<T> = block()
@@ -1,14 +0,0 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
fun <T> foo() {
val x = arrayOfNulls<T>(5)
}
inline fun <reified T> bar() {
val x = arrayOfNulls<T>(5)
}
fun baz() {
bar<Int>()
val x: Array<Int?> = arrayOfNulls(5)
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_VARIABLE
fun <T> foo() {
@@ -1,9 +0,0 @@
class Inv<T>
inline operator fun <reified T> Inv<T>.invoke() {}
operator fun <K> Inv<K>.get(i: Int): Inv<K> = this
fun <K> test(a: Inv<K>) {
a[1]()
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
class Inv<T>
inline operator fun <reified T> Inv<T>.invoke() {}
@@ -1440,6 +1440,20 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.TYPE_PARAMETER_AS_REIFIED_ARRAY) { firDiagnostic ->
TypeParameterAsReifiedArrayImpl(
firSymbolBuilder.classifierBuilder.buildTypeParameterSymbol(firDiagnostic.a.fir),
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.TYPE_PARAMETER_AS_REIFIED_ARRAY_WARNING) { firDiagnostic ->
TypeParameterAsReifiedArrayWarningImpl(
firSymbolBuilder.classifierBuilder.buildTypeParameterSymbol(firDiagnostic.a.fir),
firDiagnostic as FirPsiDiagnostic,
token,
)
}
add(FirErrors.FINAL_UPPER_BOUND) { firDiagnostic ->
FinalUpperBoundImpl(
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
@@ -1024,6 +1024,16 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val typeParameter: KtTypeParameterSymbol
}
abstract class TypeParameterAsReifiedArray : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = TypeParameterAsReifiedArray::class
abstract val typeParameter: KtTypeParameterSymbol
}
abstract class TypeParameterAsReifiedArrayWarning : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = TypeParameterAsReifiedArrayWarning::class
abstract val typeParameter: KtTypeParameterSymbol
}
abstract class FinalUpperBound : KtFirDiagnostic<KtTypeReference>() {
override val diagnosticClass get() = FinalUpperBound::class
abstract val type: KtType
@@ -1643,6 +1643,22 @@ internal class TypeParameterAsReifiedImpl(
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class TypeParameterAsReifiedArrayImpl(
override val typeParameter: KtTypeParameterSymbol,
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.TypeParameterAsReifiedArray(), KtAbstractFirDiagnostic<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class TypeParameterAsReifiedArrayWarningImpl(
override val typeParameter: KtTypeParameterSymbol,
firDiagnostic: FirPsiDiagnostic,
override val token: ValidityToken,
) : KtFirDiagnostic.TypeParameterAsReifiedArrayWarning(), KtAbstractFirDiagnostic<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
}
internal class FinalUpperBoundImpl(
override val type: KtType,
firDiagnostic: FirPsiDiagnostic,