[K/N] Implement frontend checkers for usages forward declaration type

Forward declaration type doesn't exist in runtime. This restricts
its possible usages.

^KT-59764
This commit is contained in:
Pavel Kunyavskiy
2023-07-04 10:42:02 +02:00
committed by Space Team
parent 27043bd8a6
commit 6da3ecceab
20 changed files with 456 additions and 3 deletions
@@ -7,12 +7,14 @@ package org.jetbrains.kotlin.fir.checkers.generator.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.DiagnosticList
import org.jetbrains.kotlin.fir.checkers.generator.diagnostics.model.PositioningStrategy
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
@@ -65,5 +67,19 @@ object NATIVE_DIAGNOSTICS_LIST : DiagnosticList("FirNativeErrors") {
val INVALID_OBJC_HIDES_TARGETS by error<KtElement>()
val INVALID_REFINES_IN_SWIFT_TARGETS by error<KtElement>()
val SUBTYPE_OF_HIDDEN_FROM_OBJC by error<KtElement>()
val CANNOT_CHECK_FOR_FORWARD_DECLARATION by error<KtElement>() {
parameter<ConeKotlinType>("type")
}
val UNCHECKED_CAST_TO_FORWARD_DECLARATION by warning<KtElement> {
parameter<ConeKotlinType>("sourceType")
parameter<ConeKotlinType>("destinationType")
}
val FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT by error<KtElement> {
parameter<ConeKotlinType>("type")
}
val FORWARD_DECLARATION_AS_CLASS_LITERAL by error<KtElement> {
parameter<ConeKotlinType>("type")
}
}
}
@@ -15,6 +15,7 @@ dependencies {
implementation(project(":compiler:frontend"))
implementation(project(":compiler:psi"))
compileOnly(project(":core:compiler.common.native"))
compileOnly(commonDependency("org.jetbrains.kotlin:kotlin-reflect")) { isTransitive = false }
compileOnly(intellijCore())
}
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
@@ -47,6 +48,10 @@ object FirNativeErrors {
val INVALID_OBJC_HIDES_TARGETS by error0<KtElement>()
val INVALID_REFINES_IN_SWIFT_TARGETS by error0<KtElement>()
val SUBTYPE_OF_HIDDEN_FROM_OBJC by error0<KtElement>()
val CANNOT_CHECK_FOR_FORWARD_DECLARATION by error1<KtElement, ConeKotlinType>()
val UNCHECKED_CAST_TO_FORWARD_DECLARATION by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
val FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT by error1<KtElement, ConeKotlinType>()
val FORWARD_DECLARATION_AS_CLASS_LITERAL by error1<KtElement, ConeKotlinType>()
init {
RootDiagnosticRendererFactory.registerFactory(FirNativeErrorsDefaultMessages)
@@ -8,9 +8,14 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics.native
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.diagnostics.KtDiagnosticRenderers.TO_STRING
import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.SYMBOLS
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.CANNOT_CHECK_FOR_FORWARD_DECLARATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.EMPTY_OBJC_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.FORWARD_DECLARATION_AS_CLASS_LITERAL
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INAPPLICABLE_EXACT_OBJC_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INAPPLICABLE_OBJC_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INAPPLICABLE_SHARED_IMMUTABLE_PROPERTY
@@ -33,6 +38,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.REDU
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.NON_LITERAL_OBJC_NAME_ARG
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.SUBTYPE_OF_HIDDEN_FROM_OBJC
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.THROWS_LIST_EMPTY
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.UNCHECKED_CAST_TO_FORWARD_DECLARATION
object FirNativeErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
override val MAP = KtDiagnosticFactoryToRendererMap("FIR").also { map ->
@@ -79,5 +85,28 @@ object FirNativeErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
map.put(MISSING_EXACT_OBJC_NAME, "Exact @ObjCName is required to have an ObjC name")
map.put(NON_LITERAL_OBJC_NAME_ARG, "@ObjCName accepts only literal string and boolean values")
map.put(SUBTYPE_OF_HIDDEN_FROM_OBJC, "Only @HiddenFromObjC declaration can be a subtype of @HiddenFromObjC declaration")
map.put(
CANNOT_CHECK_FOR_FORWARD_DECLARATION,
"Cannot check for forward declaration: ''{0}''",
FirDiagnosticRenderers.RENDER_TYPE
)
map.put(
UNCHECKED_CAST_TO_FORWARD_DECLARATION,
"Unchecked cast to forward declaration: ''{0}'' to ''{1}''",
FirDiagnosticRenderers.RENDER_TYPE,
FirDiagnosticRenderers.RENDER_TYPE
)
map.put(
FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT,
"Cannot pass forward declaration ''{0}'' for reified type parameter",
FirDiagnosticRenderers.RENDER_TYPE
)
map.put(
FORWARD_DECLARATION_AS_CLASS_LITERAL,
"Can't refer to forward declaration ''{0}'' from class literal",
FirDiagnosticRenderers.RENDER_TYPE
)
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2023 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.
*/
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirGetClassCallChecker
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors
import org.jetbrains.kotlin.fir.analysis.native.checkers.forwardDeclarationKindOrNull
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.types.coneType
object FirNativeForwardDeclarationGetClassCallChecker : FirGetClassCallChecker() {
override fun check(expression: FirGetClassCall, context: CheckerContext, reporter: DiagnosticReporter) {
val declarationToCheck = expression.argument.typeRef.toRegularClassSymbol(context.session) ?: return
if (expression.arguments.firstOrNull() !is FirResolvedQualifier) {
return
}
if (declarationToCheck.forwardDeclarationKindOrNull() != null) {
reporter.reportOn(
expression.source,
FirNativeErrors.FORWARD_DECLARATION_AS_CLASS_LITERAL,
expression.argument.typeRef.coneType,
context,
)
}
}
}
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2023 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.native.checkers
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessExpressionChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.types.toConeTypeProjection
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
import org.jetbrains.kotlin.fir.types.type
object FirNativeForwardDeclarationReifiedChecker : FirQualifiedAccessExpressionChecker() {
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
val calleeReference = expression.calleeReference
val typeArguments = expression.typeArguments
val typeParameters = calleeReference.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 ?: calleeReference.source ?: continue
val typeArgument = typeArgumentProjection.toConeTypeProjection().type ?: continue
val typeParameter = typeParameters[index]
if (typeParameter.isReified && typeArgument.toRegularClassSymbol(context.session)?.forwardDeclarationKindOrNull() != null) {
reporter.reportOn(
source,
FirNativeErrors.FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT,
typeArgument,
context,
)
}
}
}
}
@@ -0,0 +1,59 @@
/*
* Copyright 2010-2023 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.native.checkers
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirTypeOperatorCallChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.resolve.lookupSuperTypes
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.coneType
object FirNativeForwardDeclarationTypeOperatorChecker : FirTypeOperatorCallChecker() {
override fun check(expression: FirTypeOperatorCall, context: CheckerContext, reporter: DiagnosticReporter) {
val targetTypeRef = expression.conversionTypeRef
val declarationToCheck = targetTypeRef.toRegularClassSymbol(context.session) ?: return
val fwdKind = declarationToCheck.forwardDeclarationKindOrNull() ?: return
when (expression.operation) {
FirOperation.AS, FirOperation.SAFE_AS -> {
val sourceTypeRef = expression.argument.typeRef
val sourceClass = sourceTypeRef.toRegularClassSymbol(context.session)
// It can make sense to avoid warning if sourceClass is subclass of class with such property,
// but for the sake of simplicity, we don't do it now.
if (sourceClass != null && sourceClass.classKind == fwdKind.classKind && sourceClass.name == declarationToCheck.name) {
val supers = lookupSuperTypes(
symbol = sourceClass,
lookupInterfaces = true,
deep = true,
useSiteSession = context.session,
)
if (supers.any { it.classId == fwdKind.matchSuperClassId }) {
return
}
}
reporter.reportOn(
expression.source,
FirNativeErrors.UNCHECKED_CAST_TO_FORWARD_DECLARATION,
expression.argument.typeRef.coneType,
sourceTypeRef.coneType,
context,
)
}
FirOperation.IS, FirOperation.NOT_IS -> reporter.reportOn(
expression.source,
FirNativeErrors.CANNOT_CHECK_FOR_FORWARD_DECLARATION,
targetTypeRef.coneType,
context,
)
else -> {}
}
}
}
@@ -11,7 +11,9 @@ import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.NativeForwardDeclarationKind
fun DiagnosticReporter.reportIfHasAnnotation(
declaration: FirDeclaration,
@@ -23,4 +25,7 @@ fun DiagnosticReporter.reportIfHasAnnotation(
if (annotation != null) {
reportOn(annotation.source, error, context)
}
}
}
fun FirRegularClassSymbol.forwardDeclarationKindOrNull(): NativeForwardDeclarationKind? =
NativeForwardDeclarationKind.packageFqNameToKind[classId.packageFqName]
@@ -5,7 +5,19 @@
package org.jetbrains.kotlin.fir.analysis.native.checkers
import FirNativeForwardDeclarationGetClassCallChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirGetClassCallChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessExpressionChecker
object NativeExpressionCheckers : ExpressionCheckers() {
override val typeOperatorCallCheckers = setOf(
FirNativeForwardDeclarationTypeOperatorChecker,
)
override val getClassCallCheckers = setOf(
FirNativeForwardDeclarationGetClassCallChecker
)
override val qualifiedAccessExpressionCheckers = setOf(
FirNativeForwardDeclarationReifiedChecker
)
}
@@ -0,0 +1,104 @@
// FIR_IDENTICAL
// WITH_STDLIB
// FILE: stdlib.kt
package kotlinx.cinterop
abstract class CStructVar
interface ObjCObject
abstract class ObjCObjectBase : ObjCObject
// FILE: cnames.kt
package cnames.structs
class FwdStruct {}
// FILE: objcnamesClasses.kt
package objcnames.classes
class FwdObjcClass {}
// FILE: objcnamesProtocols.kt
package objcnames.protocols
interface FwdProtocol {}
// FILE: lib.kt
package lib
import kotlinx.cinterop.*
class FwdStruct : CStructVar()
class FwdObjcClass : ObjCObjectBase()
interface FwdProtocol : ObjCObject
// FILE: lib2.kt
package lib2
class FwdStruct
class FwdObjcClass
interface FwdProtocol
// FILE: main.kt
// this inspections differs in K1/K2, but unrelated to what is tested in this test
@file:Suppress("CAST_NEVER_SUCCEEDS", "UNUSED_PARAMETER", "UNUSED_EXPRESSION", "UNUSED_VARIABLE", "INCOMPATIBLE_TYPES")
fun testUnckeckedAsFromAny(x: Any) {
<!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x as? cnames.structs.FwdStruct<!>
<!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x as? objcnames.classes.FwdObjcClass<!>
<!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x as? objcnames.protocols.FwdProtocol<!>
if (1 > 0) { <!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x as cnames.structs.FwdStruct<!> }
if (1 > 0) { <!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x as objcnames.classes.FwdObjcClass<!> }
if (1 > 0) { <!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x as objcnames.protocols.FwdProtocol<!> }
}
fun testIs(x: Any) : Int {
return when {
<!CANNOT_CHECK_FOR_FORWARD_DECLARATION!>x is cnames.structs.FwdStruct<!> -> 1
<!CANNOT_CHECK_FOR_FORWARD_DECLARATION!>x is objcnames.classes.FwdObjcClass<!> -> 2
<!CANNOT_CHECK_FOR_FORWARD_DECLARATION!>x is objcnames.protocols.FwdProtocol<!> -> 3
else -> 4
}
}
fun testIs2(x: lib.FwdStruct) = <!CANNOT_CHECK_FOR_FORWARD_DECLARATION!>x is cnames.structs.FwdStruct<!>
fun testIs3(x: lib.FwdObjcClass) = <!CANNOT_CHECK_FOR_FORWARD_DECLARATION!>x is objcnames.classes.FwdObjcClass<!>
fun testIs4(x: lib.FwdProtocol) = <!CANNOT_CHECK_FOR_FORWARD_DECLARATION!>x is objcnames.protocols.FwdProtocol<!>
fun testClass1(x : cnames.structs.FwdStruct) = x::class
fun testClass2(x : objcnames.classes.FwdObjcClass) = x::class
fun testClass3(x : objcnames.protocols.FwdProtocol) = x::class
fun testClass4() {
<!FORWARD_DECLARATION_AS_CLASS_LITERAL!>cnames.structs.FwdStruct::class<!>
<!FORWARD_DECLARATION_AS_CLASS_LITERAL!>objcnames.classes.FwdObjcClass::class<!>
<!FORWARD_DECLARATION_AS_CLASS_LITERAL!>objcnames.protocols.FwdProtocol::class<!>
}
inline fun <reified T> inlineF(x: T) {}
fun testInline1(x : cnames.structs.FwdStruct) = <!FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT!>inlineF<!>(x)
fun testInline2(x : objcnames.classes.FwdObjcClass) = <!FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT!>inlineF<!>(x)
fun testInline3(x : objcnames.protocols.FwdProtocol) = <!FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT!>inlineF<!>(x)
fun testInline4() {
val a : (cnames.structs.FwdStruct) -> Unit = ::<!FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT!>inlineF<!>
val b : (objcnames.classes.FwdObjcClass) -> Unit = ::<!FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT!>inlineF<!>
val c : (objcnames.protocols.FwdProtocol) -> Unit = ::<!FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT!>inlineF<!>
}
fun testCheckedAs1(x : lib.FwdStruct) = x as cnames.structs.FwdStruct
fun testCheckedAs2(x : lib.FwdObjcClass) = x as objcnames.classes.FwdObjcClass
fun testCheckedAs3(x : lib.FwdProtocol) = x as objcnames.protocols.FwdProtocol
fun testUnCheckedAs1(x : lib2.FwdStruct) = <!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x as cnames.structs.FwdStruct<!>
fun testUnCheckedAs2(x : lib2.FwdObjcClass) = <!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x as objcnames.classes.FwdObjcClass<!>
fun testUnCheckedAs3(x : lib2.FwdProtocol) = <!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x as objcnames.protocols.FwdProtocol<!>
fun testUnCheckedAs4(x : lib.FwdStruct) = <!UNCHECKED_CAST_TO_FORWARD_DECLARATION!>x as objcnames.classes.FwdObjcClass<!>
+1
View File
@@ -6,6 +6,7 @@ plugins {
dependencies {
compileOnly(project(":compiler:frontend"))
compileOnly(project(":compiler:frontend.java"))
compileOnly(project(":core:compiler.common.native"))
compileOnly(intellijCore())
api(project(":native:kotlin-native-utils"))
}
@@ -5,10 +5,15 @@
package org.jetbrains.kotlin.descriptors.konan
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME
import org.jetbrains.kotlin.name.NativeForwardDeclarationKind
import org.jetbrains.kotlin.name.Name
val NATIVE_STDLIB_MODULE_NAME = Name.special("<$KONAN_STDLIB_NAME>")
fun ModuleDescriptor.isNativeStdlib() = name == NATIVE_STDLIB_MODULE_NAME
fun ClassDescriptor.getForwardDeclarationKindOrNull() =
NativeForwardDeclarationKind.packageFqNameToKind[(containingDeclaration as? PackageFragmentDescriptor)?.fqName]
@@ -69,6 +69,27 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
ErrorsNative.SUBTYPE_OF_HIDDEN_FROM_OBJC,
"Only @HiddenFromObjC declaration can be a subtype of @HiddenFromObjC declaration"
)
put(
ErrorsNative.CANNOT_CHECK_FOR_FORWARD_DECLARATION,
"Cannot check for forward declaration: ''{0}''",
Renderers.RENDER_TYPE
)
put(ErrorsNative.UNCHECKED_CAST_TO_FORWARD_DECLARATION,
"Unchecked cast to forward declaration: ''{0}'' to ''{1}''",
Renderers.RENDER_TYPE,
Renderers.RENDER_TYPE
)
put(
ErrorsNative.FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT,
"Cannot pass forward declaration ''{0}'' for reified type parameter",
Renderers.RENDER_TYPE
)
put(
ErrorsNative.FORWARD_DECLARATION_AS_CLASS_LITERAL,
"Can't refer to forward declaration ''{0}'' from class literal",
Renderers.RENDER_TYPE
)
}
}
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.types.KotlinType
object ErrorsNative {
@JvmField
@@ -60,6 +61,14 @@ object ErrorsNative {
val INVALID_REFINES_IN_SWIFT_TARGETS = DiagnosticFactory0.create<KtElement>(Severity.ERROR)
@JvmField
val SUBTYPE_OF_HIDDEN_FROM_OBJC = DiagnosticFactory0.create<KtElement>(Severity.ERROR)
@JvmField
val CANNOT_CHECK_FOR_FORWARD_DECLARATION = DiagnosticFactory1.create<PsiElement, KotlinType>(Severity.ERROR)
@JvmField
val UNCHECKED_CAST_TO_FORWARD_DECLARATION = DiagnosticFactory2.create<PsiElement, KotlinType, KotlinType>(Severity.WARNING)
@JvmField
val FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT = DiagnosticFactory1.create<PsiElement, KotlinType>(Severity.ERROR)
@JvmField
val FORWARD_DECLARATION_AS_CLASS_LITERAL = DiagnosticFactory1.create<PsiElement, KotlinType>(Severity.ERROR)
init {
Errors.Initializer.initializeFactoryNames(ErrorsNative::class.java)
@@ -0,0 +1,56 @@
/*
* Copyright 2010-2023 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.resolve.konan.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.konan.getForwardDeclarationKindOrNull
import org.jetbrains.kotlin.psi.KtClassLiteralExpression
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.checkers.RttiExpressionChecker
import org.jetbrains.kotlin.resolve.calls.checkers.RttiExpressionInformation
import org.jetbrains.kotlin.resolve.calls.checkers.RttiOperation
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.expressions.ClassLiteralChecker
class NativeForwardDeclarationRttiChecker : RttiExpressionChecker, ClassLiteralChecker {
override fun check(rttiInformation: RttiExpressionInformation, reportOn: PsiElement, trace: BindingTrace) {
val sourceType = rttiInformation.sourceType
val targetType = rttiInformation.targetType
val targetDescriptor = targetType?.constructor?.declarationDescriptor
if (sourceType != null && targetDescriptor is ClassDescriptor) {
val kind = targetDescriptor.getForwardDeclarationKindOrNull() ?: return
when (rttiInformation.operation) {
RttiOperation.IS,
RttiOperation.NOT_IS -> trace.report(ErrorsNative.CANNOT_CHECK_FOR_FORWARD_DECLARATION.on(reportOn, targetType))
RttiOperation.AS,
RttiOperation.SAFE_AS -> {
// It can make sense to avoid warning if sourceClass is subclass of class with such property,
// but for the sake of simplicity, we don't do it now.
val sourceDescriptor = sourceType.constructor.declarationDescriptor as? ClassDescriptor
val isAllowedCast = sourceDescriptor != null &&
sourceDescriptor.name == targetDescriptor.name &&
sourceDescriptor.kind == kind.classKind &&
sourceDescriptor.getAllSuperClassifiers().any { it.fqNameSafe == kind.matchSuperClassFqName }
if (!isAllowedCast) {
trace.report(ErrorsNative.UNCHECKED_CAST_TO_FORWARD_DECLARATION.on(reportOn, sourceType, targetType))
}
}
}
}
}
override fun check(expression: KtClassLiteralExpression, type: KotlinType, context: ResolutionContext<*>) {
val descriptor = type.constructor.declarationDescriptor as? ClassDescriptor
if (descriptor?.getForwardDeclarationKindOrNull() != null) {
context.trace.report(ErrorsNative.FORWARD_DECLARATION_AS_CLASS_LITERAL.on(expression, type))
}
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2023 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.resolve.konan.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.descriptors.konan.*
class NativeReifiedForwardDeclarationChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val typeArgumentList = resolvedCall.call.typeArgumentList?.arguments
for ((typeParam, typeArg) in resolvedCall.typeArguments) {
if (!typeParam.isReified) continue
val typeArgDescriptor = typeArg.constructor.declarationDescriptor
if (typeArgDescriptor is ClassDescriptor && typeArgDescriptor.getForwardDeclarationKindOrNull() != null) {
val typeArgumentPsi = if (typeArgumentList != null) {
typeArgumentList[typeParam.index].typeReference
} else {
resolvedCall.call.calleeExpression
}
context.trace.report(ErrorsNative.FORWARD_DECLARATION_AS_REIFIED_TYPE_ARGUMENT.on(typeArgumentPsi!!, typeArg))
}
}
}
}
@@ -21,19 +21,21 @@ import org.jetbrains.kotlin.resolve.konan.diagnostics.*
object NativePlatformConfigurator : PlatformConfiguratorBase(
additionalCallCheckers = listOf(
SuperCallWithDefaultArgumentsChecker(),
LateinitIntrinsicApplicabilityChecker(isWarningInPre19 = true)
LateinitIntrinsicApplicabilityChecker(isWarningInPre19 = true),
NativeReifiedForwardDeclarationChecker(),
),
additionalDeclarationCheckers = listOf(
NativeThrowsChecker, NativeSharedImmutableChecker,
NativeThreadLocalChecker,
NativeObjCNameChecker, NativeObjCNameOverridesChecker,
NativeObjCRefinementChecker, NativeObjCRefinementAnnotationChecker,
NativeObjCRefinementOverridesChecker, NativeHiddenFromObjCInheritanceChecker
NativeObjCRefinementOverridesChecker, NativeHiddenFromObjCInheritanceChecker,
)
) {
override fun configureModuleComponents(container: StorageComponentContainer) {
container.useInstance(NativeInliningRule)
container.useImpl<NativeIdentifierChecker>()
container.useImpl<NativeForwardDeclarationRttiChecker>()
}
override fun configureModuleDependentCheckers(container: StorageComponentContainer) {
@@ -30,6 +30,12 @@ public class DiagnosticsNativeTestGenerated extends AbstractDiagnosticsNativeTes
runTest("compiler/testData/diagnostics/nativeTests/cloneableInNative.kt");
}
@Test
@TestMetadata("forwardDeclarations.kt")
public void testForwardDeclarations() throws Exception {
runTest("compiler/testData/diagnostics/nativeTests/forwardDeclarations.kt");
}
@Test
@TestMetadata("identifiers.kt")
public void testIdentifiers() throws Exception {
@@ -34,6 +34,12 @@ public class FirLightTreeOldFrontendNativeDiagnosticsTestGenerated extends Abstr
runTest("compiler/testData/diagnostics/nativeTests/cloneableInNative.kt");
}
@Test
@TestMetadata("forwardDeclarations.kt")
public void testForwardDeclarations() throws Exception {
runTest("compiler/testData/diagnostics/nativeTests/forwardDeclarations.kt");
}
@Test
@TestMetadata("identifiers.kt")
public void testIdentifiers() throws Exception {
@@ -34,6 +34,12 @@ public class FirPsiOldFrontendNativeDiagnosticsTestGenerated extends AbstractFir
runTest("compiler/testData/diagnostics/nativeTests/cloneableInNative.kt");
}
@Test
@TestMetadata("forwardDeclarations.kt")
public void testForwardDeclarations() throws Exception {
runTest("compiler/testData/diagnostics/nativeTests/forwardDeclarations.kt");
}
@Test
@TestMetadata("identifiers.kt")
public void testIdentifiers() throws Exception {