FIR: check multiple vararg param and forbidden vararg type

This commit is contained in:
Tianyu Geng
2021-02-22 18:24:46 -08:00
committed by Mikhail Glukhikh
parent 0e9474342d
commit bdeecfc188
22 changed files with 130 additions and 83 deletions
@@ -292,6 +292,10 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
// TODO: val ANONYMOUS_FUNCTION_WITH_NAME by error1<FirSourceElement, PsiElement, Name>(SourceElementPositioningStrategies.DECLARATION_NAME)
val ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE by error<FirSourceElement, KtParameter>(PositioningStrategy.PARAMETER_DEFAULT_VALUE)
val USELESS_VARARG_ON_PARAMETER by warning<FirSourceElement, KtParameter>()
val MULTIPLE_VARARG_PARAMETERS by error<FirSourceElement, KtParameter>(PositioningStrategy.PARAMETER_VARARG_MODIFIER)
val FORBIDDEN_VARARG_PARAMETER_TYPE by error<FirSourceElement, KtParameter>(PositioningStrategy.PARAMETER_VARARG_MODIFIER) {
parameter<ConeKotlinType>("varargParameterType")
}
}
val PROPERTIES_AND_ACCESSORS by object : DiagnosticGroup("Properties & accessors") {
@@ -198,6 +198,8 @@ object FirErrors {
val FUNCTION_DECLARATION_WITH_NO_NAME by error0<FirSourceElement, KtFunction>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE)
val ANONYMOUS_FUNCTION_PARAMETER_WITH_DEFAULT_VALUE by error0<FirSourceElement, KtParameter>(SourceElementPositioningStrategies.PARAMETER_DEFAULT_VALUE)
val USELESS_VARARG_ON_PARAMETER by warning0<FirSourceElement, KtParameter>()
val MULTIPLE_VARARG_PARAMETERS by error0<FirSourceElement, KtParameter>(SourceElementPositioningStrategies.PARAMETER_VARARG_MODIFIER)
val FORBIDDEN_VARARG_PARAMETER_TYPE by error1<FirSourceElement, KtParameter, ConeKotlinType>(SourceElementPositioningStrategies.PARAMETER_VARARG_MODIFIER)
// Properties & accessors
val ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS by error2<FirSourceElement, KtModifierListOwner, FirMemberDeclaration, FirMemberDeclaration>(SourceElementPositioningStrategies.MODALITY_MODIFIER)
@@ -121,9 +121,11 @@ fun ConeClassLikeType.toRegularClass(session: FirSession): FirRegularClass? {
* or null of something goes wrong.
*/
fun ConeKotlinType.toRegularClass(session: FirSession): FirRegularClass? {
return safeAs<ConeClassLikeType>()?.toRegularClass(session)
return safeAs<ConeClassLikeType>()?.fullyExpandedType(session)?.toRegularClass(session)
}
fun ConeKotlinType.isInline(session: FirSession) : Boolean = toRegularClass(session)?.isInline == true
/**
* Returns the FirRegularClass associated with this
* or null of something goes wrong.
@@ -0,0 +1,50 @@
/*
* 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.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.isInline
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.FirFunction
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.arrayElementType
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isUnsignedTypeOrNullableUnsignedType
import org.jetbrains.kotlin.types.AbstractTypeChecker
object FirFunctionParameterChecker : FirFunctionChecker() {
override fun check(declaration: FirFunction<*>, context: CheckerContext, reporter: DiagnosticReporter) {
checkVarargParameters(declaration, context, reporter)
}
private fun checkVarargParameters(function: FirFunction<*>, context: CheckerContext, reporter: DiagnosticReporter) {
val varargParameters = function.valueParameters.filter { it.isVararg }
if (varargParameters.size > 1) {
for (parameter in varargParameters) {
reporter.reportOn(parameter.source ?: continue, FirErrors.MULTIPLE_VARARG_PARAMETERS, context)
}
}
val nullableNothingType = context.session.builtinTypes.nullableNothingType.coneType
for (varargParameter in varargParameters) {
val varargParameterType = varargParameter.returnTypeRef.coneType.arrayElementType() ?: continue
if (AbstractTypeChecker.isSubtypeOf(context.session.typeContext, varargParameterType, nullableNothingType) ||
(varargParameterType.isInline(context.session) && !varargParameterType.isUnsignedTypeOrNullableUnsignedType)
// Note: comparing with FE1.0, we skip checking if the type is not primitive because primitive types are not inline. That
// is any primitive values are already allowed by the inline check.
) {
reporter.reportOn(
varargParameter.source ?: continue,
FirErrors.FORBIDDEN_VARARG_PARAMETER_TYPE,
varargParameterType,
context
)
}
}
}
}
@@ -75,6 +75,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_SUPER_INT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_TYPEALIAS_EXPANDED_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_TYPE_PARAMETER_BOUND
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXTENSION_PROPERTY_WITH_BACKING_FIELD
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_VARARG_PARAMETER_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUNCTION_DECLARATION_WITH_NO_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.GENERIC_THROWABLE_SUBCLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.HIDDEN
@@ -97,6 +98,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LOCAL_INTERFACE_N
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LOCAL_OBJECT_NOT_ALLOWED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_COMPANION_OBJECTS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MISSING_VAL_ON_ANNOTATION_PARAMETER
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MULTIPLE_VARARG_PARAMETERS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NONE_APPLICABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_ABSTRACT_FUNCTION_WITH_NO_BODY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_FINAL_MEMBER_IN_FINAL_CLASS
@@ -444,6 +446,8 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
"An anonymous function is not allowed to specify default values for its parameters"
)
map.put(USELESS_VARARG_ON_PARAMETER, "Vararg on this parameter is useless")
map.put(MULTIPLE_VARARG_PARAMETERS, "Multiple vararg-parameters are prohibited")
map.put(FORBIDDEN_VARARG_PARAMETER_TYPE, "Forbidden vararg parameter type: {0}", RENDER_TYPE)
// Properties & accessors
map.put(
@@ -69,9 +69,15 @@ val ConeKotlinType.isArrayType: Boolean
return isBuiltinType(StandardClassIds.Array, false) ||
StandardClassIds.primitiveArrayTypeByElementType.values.any { isBuiltinType(it, false) }
}
private val builtinUnsignedTypes = setOf(StandardClassIds.UInt, StandardClassIds.UByte, StandardClassIds.ULong, StandardClassIds.UShort)
val ConeKotlinType.isUnsignedTypeOrNullableUnsignedType: Boolean get() = isAnyOfBuiltinType(builtinUnsignedTypes)
private fun ConeKotlinType.isBuiltinType(classId: ClassId, isNullable: Boolean): Boolean {
if (this !is ConeClassLikeType) return false
return lookupTag.classId == classId && type.isNullable == isNullable
}
private fun ConeKotlinType.isAnyOfBuiltinType(classIds: Set<ClassId>): Boolean {
if (this !is ConeClassLikeType) return false
return lookupTag.classId in classIds
}
@@ -29,6 +29,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
override val functionCheckers: Set<FirFunctionChecker> = setOf(
FirContractChecker,
FirFunctionNameChecker,
FirFunctionParameterChecker,
)
override val propertyCheckers: Set<FirPropertyChecker> = setOf(
@@ -63,11 +63,11 @@ annotation class InAnn10(val p1: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>String?<!
annotation class InAnn11(val p1: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>Ann1?<!>)
annotation class InAnn12(val p1: <!NULLABLE_TYPE_OF_ANNOTATION_MEMBER!>MyEnum?<!>)
annotation class InAnn13(vararg val p1: String,
vararg val p2: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Class<*><!>,
vararg val p3: MyEnum,
vararg val p4: Ann1,
vararg val p5: Int)
annotation class InAnn13(<!MULTIPLE_VARARG_PARAMETERS!>vararg<!> val p1: String,
<!MULTIPLE_VARARG_PARAMETERS!>vararg<!> val p2: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Class<*><!>,
<!MULTIPLE_VARARG_PARAMETERS!>vararg<!> val p3: MyEnum,
<!MULTIPLE_VARARG_PARAMETERS!>vararg<!> val p4: Ann1,
<!MULTIPLE_VARARG_PARAMETERS!>vararg<!> val p5: Int)
enum class MyEnum {
A
@@ -1,35 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER -PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED
fun test(vararg x1: Int, vararg x2: Int) {
fun test2(vararg x1: Int, vararg x2: Int) {
class LocalClass(vararg x1: Int, vararg x2: Int) {
constructor(vararg x1: Int, vararg x2: Int, xx: Int) {}
}
fun test3(vararg x1: Int, vararg x2: Int) {}
}
}
fun Any.test(vararg x1: Int, vararg x2: Int, vararg x3: Int) {}
interface I {
fun test(vararg x1: Int, vararg x2: Int)
}
abstract class C(vararg x1: Int, vararg x2: Int, b: Boolean) {
fun test(vararg x1: Int, vararg x2: Int) {}
abstract fun test2(vararg x1: Int, vararg x2: Int)
class CC(vararg x1: Int, vararg x2: Int, b: Boolean) {
constructor(vararg x1: Int, vararg x2: Int) {}
fun test(vararg x1: Int, vararg x2: Int) {}
}
}
object O {
fun test(vararg x1: Int, vararg x2: Int) {}
class CC(vararg x1: Int, vararg x2: Int, b: Boolean) {
constructor(vararg x1: Int, vararg x2: Int) {}
fun test(vararg x1: Int, vararg x2: Int) {}
}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER -PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED
fun test(<!MULTIPLE_VARARG_PARAMETERS!>vararg<!> x1: Int, <!MULTIPLE_VARARG_PARAMETERS!>vararg<!> x2: Int) {
fun test2(<!MULTIPLE_VARARG_PARAMETERS!>vararg<!> x1: Int, <!MULTIPLE_VARARG_PARAMETERS!>vararg<!> x2: Int) {
@@ -2,11 +2,11 @@ fun f(
val a: Int,
var b: Int,
c: Int,
vararg var d: Int,
vararg val e: Int,
vararg f: Int
<!MULTIPLE_VARARG_PARAMETERS!>vararg<!> var d: Int,
<!MULTIPLE_VARARG_PARAMETERS!>vararg<!> val e: Int,
<!MULTIPLE_VARARG_PARAMETERS!>vararg<!> f: Int
) {
a + b + c + d[0] + e[0] + f[0] // to avoid 'unused parameter'
}
}
@@ -1,12 +0,0 @@
// !LANGUAGE: +InlineClasses
import kotlin.reflect.KClass
inline class MyInt(val x: Int)
inline class MyString(val x: String)
annotation class Ann1(val a: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>MyInt<!>)
annotation class Ann2(val a: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Array<MyString><!>)
annotation class Ann3(vararg val a: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>MyInt<!>)
annotation class Ann4(val a: KClass<MyInt>)
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !LANGUAGE: +InlineClasses
import kotlin.reflect.KClass
@@ -3,19 +3,19 @@
inline class Foo(val x: Int)
fun f1(vararg a: Foo) {}
fun f2(vararg a: Foo?) {}
fun f1(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> a: Foo) {}
fun f2(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> a: Foo?) {}
class A {
fun f3(a0: Int, vararg a1: Foo) {
fun f4(vararg a: Foo) {}
fun f3(a0: Int, <!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> a1: Foo) {
fun f4(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> a: Foo) {}
val g = fun (<!USELESS_VARARG_ON_PARAMETER!>vararg v: Foo<!>) {}
}
}
class B(vararg val s: Foo) {
constructor(a: Int, vararg s: Foo) : this(*s)
class B(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> val s: Foo) {
constructor(a: Int, <!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> s: Foo) : this(*s)
}
annotation class Ann(vararg val f: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Foo<!>)
annotation class Ann(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> val f: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>Foo<!>)
@@ -9,4 +9,4 @@ class ValueParam(vararg val a: ULong)
annotation class Ann(vararg val a: <!INVALID_TYPE_OF_ANNOTATION_MEMBER!>UInt<!>)
fun array(vararg a: UIntArray) {}
fun array(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> a: UIntArray) {}
@@ -1,15 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun testVarargOfNothing(vararg v: Nothing) {}
fun testVarargOfNNothing(vararg v: Nothing?) {}
fun <T : Nothing?> testVarargOfT(vararg v: T) {}
fun outer() {
fun testVarargOfNothing(vararg v: Nothing) {}
fun testVarargOfNNothing(vararg v: Nothing?) {}
fun <T : Nothing?> testVarargOfT(vararg v: T) {}
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
fun testVarargOfNothing(<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> v: Nothing) {}
@@ -11,7 +11,7 @@ fun params(
r3: ResultAlias<String>,
r4: List<Result<Int>>,
r5: InlineResult<Int>,
vararg r6: Result<Int>
<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> r6: Result<Int>
) {}
class CtorParams(r1: Result<Int>)
@@ -11,7 +11,7 @@ fun params(
r3: ResultAlias<String>,
r4: List<Result<Int>>,
r5: InlineResult<Int>,
vararg r6: Result<Int>
<!FORBIDDEN_VARARG_PARAMETER_TYPE!>vararg<!> r6: Result<Int>
) {}
class CtorParams(r1: Result<Int>)
@@ -840,6 +840,19 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.MULTIPLE_VARARG_PARAMETERS) { firDiagnostic ->
MultipleVarargParametersImpl(
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.FORBIDDEN_VARARG_PARAMETER_TYPE) { firDiagnostic ->
ForbiddenVarargParameterTypeImpl(
firSymbolBuilder.buildKtType(firDiagnostic.a),
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS) { firDiagnostic ->
AbstractPropertyInNonAbstractClassImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a as FirDeclaration),
@@ -594,6 +594,15 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = UselessVarargOnParameter::class
}
abstract class MultipleVarargParameters : KtFirDiagnostic<KtParameter>() {
override val diagnosticClass get() = MultipleVarargParameters::class
}
abstract class ForbiddenVarargParameterType : KtFirDiagnostic<KtParameter>() {
override val diagnosticClass get() = ForbiddenVarargParameterType::class
abstract val varargParameterType: KtType
}
abstract class AbstractPropertyInNonAbstractClass : KtFirDiagnostic<KtModifierListOwner>() {
override val diagnosticClass get() = AbstractPropertyInNonAbstractClass::class
abstract val property: KtSymbol
@@ -952,6 +952,21 @@ internal class UselessVarargOnParameterImpl(
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class MultipleVarargParametersImpl(
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.MultipleVarargParameters(), KtAbstractFirDiagnostic<KtParameter> {
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class ForbiddenVarargParameterTypeImpl(
override val varargParameterType: KtType,
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.ForbiddenVarargParameterType(), KtAbstractFirDiagnostic<KtParameter> {
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class AbstractPropertyInNonAbstractClassImpl(
override val property: KtSymbol,
override val containingClass: KtSymbol,