[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:
committed by
Space Team
parent
27043bd8a6
commit
6da3ecceab
+16
@@ -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())
|
||||
}
|
||||
|
||||
+5
@@ -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)
|
||||
|
||||
+29
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+34
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
@@ -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 -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -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]
|
||||
+12
@@ -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<!>
|
||||
@@ -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]
|
||||
+21
@@ -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)
|
||||
|
||||
+56
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -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) {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user