[K/N] Implement FirNativeObjCOverrideInitChecker
^KT-61564
This commit is contained in:
committed by
Space Team
parent
4a97db82b3
commit
dbba6d09ff
+9
@@ -94,5 +94,14 @@ object NATIVE_DIAGNOSTICS_LIST : DiagnosticList("FirNativeErrors") {
|
||||
parameter<String>("annotationKind")
|
||||
parameter<ConeKotlinType>("unexpectedType")
|
||||
}
|
||||
val CONSTRUCTOR_OVERRIDES_ALREADY_OVERRIDDEN_OBJC_INITIALIZER by error<KtElement>() {
|
||||
parameter<FqName>("annotation")
|
||||
}
|
||||
val CONSTRUCTOR_DOES_NOT_OVERRIDE_ANY_SUPER_CONSTRUCTOR by error<KtElement>() {
|
||||
parameter<FqName>("annotation")
|
||||
}
|
||||
val CONSTRUCTOR_MATCHES_SEVERAL_SUPER_CONSTRUCTORS by error<KtElement>() {
|
||||
parameter<FqName>("annotation")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -59,6 +59,9 @@ object FirNativeErrors {
|
||||
val MUST_NOT_HAVE_EXTENSION_RECEIVER: KtDiagnosticFactory1<String> by error1<KtElement, String>()
|
||||
val MUST_BE_OBJC_OBJECT_TYPE: KtDiagnosticFactory2<String, ConeKotlinType> by error2<KtElement, String, ConeKotlinType>()
|
||||
val MUST_BE_UNIT_TYPE: KtDiagnosticFactory2<String, ConeKotlinType> by error2<KtElement, String, ConeKotlinType>()
|
||||
val CONSTRUCTOR_OVERRIDES_ALREADY_OVERRIDDEN_OBJC_INITIALIZER: KtDiagnosticFactory1<FqName> by error1<KtElement, FqName>()
|
||||
val CONSTRUCTOR_DOES_NOT_OVERRIDE_ANY_SUPER_CONSTRUCTOR: KtDiagnosticFactory1<FqName> by error1<KtElement, FqName>()
|
||||
val CONSTRUCTOR_MATCHES_SEVERAL_SUPER_CONSTRUCTORS: KtDiagnosticFactory1<FqName> by error1<KtElement, FqName>()
|
||||
|
||||
init {
|
||||
RootDiagnosticRendererFactory.registerFactory(FirNativeErrorsDefaultMessages)
|
||||
|
||||
+18
@@ -12,6 +12,9 @@ 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.CONSTRUCTOR_DOES_NOT_OVERRIDE_ANY_SUPER_CONSTRUCTOR
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.CONSTRUCTOR_MATCHES_SEVERAL_SUPER_CONSTRUCTORS
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.CONSTRUCTOR_OVERRIDES_ALREADY_OVERRIDDEN_OBJC_INITIALIZER
|
||||
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
|
||||
@@ -127,5 +130,20 @@ object FirNativeErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
|
||||
TO_STRING,
|
||||
FirDiagnosticRenderers.RENDER_TYPE
|
||||
)
|
||||
map.put(
|
||||
CONSTRUCTOR_OVERRIDES_ALREADY_OVERRIDDEN_OBJC_INITIALIZER,
|
||||
"Constructor with @{0} overrides initializer that is already overridden explicitly",
|
||||
TO_STRING
|
||||
)
|
||||
map.put(
|
||||
CONSTRUCTOR_DOES_NOT_OVERRIDE_ANY_SUPER_CONSTRUCTOR,
|
||||
"Constructor with @{0} doesn''t override any super class constructor.\nIt must completely match by parameter names and types.",
|
||||
TO_STRING
|
||||
)
|
||||
map.put(
|
||||
CONSTRUCTOR_MATCHES_SEVERAL_SUPER_CONSTRUCTORS,
|
||||
"Constructor with @{0} matches more than one of super constructors",
|
||||
TO_STRING
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors
|
||||
import org.jetbrains.kotlin.fir.backend.native.interop.getObjCInitMethod
|
||||
import org.jetbrains.kotlin.fir.backend.native.interop.isKotlinObjCClass
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isExpect
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isOverride
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.scopes.getDirectOverriddenMembers
|
||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
object FirNativeObjCOverrideInitChecker : FirClassChecker() {
|
||||
val objCOverrideInitClassId = ClassId(FqName("kotlinx.cinterop"), Name.identifier("ObjCObjectBase.OverrideInit"))
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
val session = context.session
|
||||
|
||||
fun FirConstructor.overridesConstructor(other: FirConstructor): Boolean {
|
||||
if (valueParameters.size != other.valueParameters.size)
|
||||
return false
|
||||
return valueParameters.zip(other.valueParameters).all { (thisParameter, otherParameter) ->
|
||||
thisParameter.name == otherParameter.name && thisParameter.returnTypeRef.coneType == otherParameter.returnTypeRef.coneType
|
||||
}
|
||||
}
|
||||
|
||||
fun checkCanGenerateOverrideInit(firClass: FirClass, constructor: FirConstructor) {
|
||||
val superClass = (firClass as FirRegularClass).symbol.getSuperClassSymbolOrAny(session)
|
||||
val superConstructors = superClass.fir.constructors(session).filter {
|
||||
constructor.overridesConstructor(it.fir)
|
||||
}.toList()
|
||||
|
||||
val superConstructor: FirConstructorSymbol = superConstructors.singleOrNull() ?: run {
|
||||
if (superConstructors.isEmpty())
|
||||
reporter.reportOn(
|
||||
constructor.source,
|
||||
FirNativeErrors.CONSTRUCTOR_DOES_NOT_OVERRIDE_ANY_SUPER_CONSTRUCTOR,
|
||||
objCOverrideInitClassId.asSingleFqName(),
|
||||
context
|
||||
)
|
||||
else
|
||||
reporter.reportOn(
|
||||
constructor.source,
|
||||
FirNativeErrors.CONSTRUCTOR_MATCHES_SEVERAL_SUPER_CONSTRUCTORS,
|
||||
objCOverrideInitClassId.asSingleFqName(),
|
||||
context
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
val initMethod = superConstructor.fir.getObjCInitMethod(session, ScopeSession())!!
|
||||
|
||||
// Remove fake overrides of this init method, also check for explicit overriding:
|
||||
firClass.declarations.forEach {
|
||||
if (it is FirSimpleFunction && initMethod.symbol in it.getOverriddenSymbols(context) && !it.isSubstitutionOrIntersectionOverride) {
|
||||
reporter.reportOn(
|
||||
constructor.source,
|
||||
FirNativeErrors.CONSTRUCTOR_OVERRIDES_ALREADY_OVERRIDDEN_OBJC_INITIALIZER,
|
||||
objCOverrideInitClassId.asSingleFqName(),
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun checkKotlinObjCClass(firClass: FirClass) {
|
||||
for (decl in firClass.declarations) {
|
||||
if (decl is FirConstructor && decl.annotations.hasAnnotation(objCOverrideInitClassId, session))
|
||||
checkCanGenerateOverrideInit(firClass, decl)
|
||||
}
|
||||
}
|
||||
|
||||
if (!declaration.isExpect && declaration.symbol.isKotlinObjCClass(context.session)) {
|
||||
checkKotlinObjCClass(declaration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy-pasted from plugins/noarg/noarg.k2/src/org/jetbrains/kotlin/noarg/fir/FirNoArgDeclarationChecker.kt
|
||||
private fun FirRegularClassSymbol.getSuperClassSymbolOrAny(session: FirSession): FirRegularClassSymbol {
|
||||
for (superType in resolvedSuperTypes) {
|
||||
val symbol = superType.fullyExpandedType(session).toRegularClassSymbol(session) ?: continue
|
||||
if (symbol.classKind == ClassKind.CLASS) return symbol
|
||||
}
|
||||
return session.builtinTypes.anyType.type.toRegularClassSymbol(session) ?: error("Symbol for Any not found")
|
||||
}
|
||||
|
||||
// copy-pasted from org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirInlineDeclarationChecker
|
||||
private fun FirCallableDeclaration.getOverriddenSymbols(context: CheckerContext): List<FirCallableSymbol<out FirCallableDeclaration>> {
|
||||
if (!this.isOverride) return emptyList()
|
||||
val classSymbol = this.containingClassLookupTag()?.toSymbol(context.session) as? FirClassSymbol<*> ?: return emptyList()
|
||||
val scope = classSymbol.unsubstitutedScope(context)
|
||||
//this call is needed because AbstractFirUseSiteMemberScope collect overrides in it only,
|
||||
//and not in processDirectOverriddenFunctionsWithBaseScope
|
||||
scope.processFunctionsByName(this.symbol.name) { }
|
||||
return scope.getDirectOverriddenMembers(this.symbol, true)
|
||||
}
|
||||
+1
@@ -28,6 +28,7 @@ object NativeDeclarationCheckers : DeclarationCheckers() {
|
||||
FirNativeObjCNameOverridesChecker,
|
||||
FirNativeObjCOutletChecker,
|
||||
FirNativeObjCActionChecker,
|
||||
FirNativeObjCOverrideInitChecker,
|
||||
)
|
||||
|
||||
override val regularClassCheckers: Set<FirRegularClassChecker>
|
||||
|
||||
+3
@@ -672,6 +672,9 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set<String> = setOf(
|
||||
"MUST_NOT_HAVE_EXTENSION_RECEIVER",
|
||||
"MUST_BE_OBJC_OBJECT_TYPE",
|
||||
"MUST_BE_UNIT_TYPE",
|
||||
"CONSTRUCTOR_OVERRIDES_ALREADY_OVERRIDDEN_OBJC_INITIALIZER",
|
||||
"CONSTRUCTOR_DOES_NOT_OVERRIDE_ANY_SUPER_CONSTRUCTOR",
|
||||
"CONSTRUCTOR_MATCHES_SEVERAL_SUPER_CONSTRUCTORS",
|
||||
"NESTED_EXTERNAL_DECLARATION",
|
||||
"WRONG_EXTERNAL_DECLARATION",
|
||||
"NESTED_CLASS_IN_EXTERNAL_INTERFACE",
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ internal fun FirFunction.getObjCMethodInfoFromOverriddenFunctions(session: FirSe
|
||||
* mimics ConstructorDescriptor.getObjCInitMethod()
|
||||
*/
|
||||
@OptIn(SymbolInternals::class)
|
||||
private fun FirConstructor.getObjCInitMethod(session: FirSession, scopeSession: ScopeSession): FirFunction? {
|
||||
fun FirConstructor.getObjCInitMethod(session: FirSession, scopeSession: ScopeSession): FirFunction? {
|
||||
this.annotations.getAnnotationByClassId(NativeStandardInteropNames.objCConstructorClassId, session)?.let { annotation ->
|
||||
val initSelector: String = annotation.constStringArgument("initSelector")
|
||||
val classSymbol = containingClassLookupTag()?.toSymbol(session) as FirClassSymbol<*>
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ import platform.darwin.*
|
||||
import platform.Foundation.*
|
||||
|
||||
class Zzz : NSAssertionHandler {
|
||||
@OptIn(kotlinx.cinterop.BetaInteropApi::class)
|
||||
<!CONSTRUCTOR_DOES_NOT_OVERRIDE_ANY_SUPER_CONSTRUCTOR!>@OptIn(kotlinx.cinterop.BetaInteropApi::class)
|
||||
@OverrideInit
|
||||
constructor(x: Int) { }
|
||||
constructor(x: Int) { }<!>
|
||||
}
|
||||
|
||||
+2
-2
@@ -5,9 +5,9 @@ import platform.darwin.*
|
||||
import platform.Foundation.*
|
||||
|
||||
class Zzz : NSString {
|
||||
@OptIn(kotlinx.cinterop.BetaInteropApi::class)
|
||||
<!CONSTRUCTOR_OVERRIDES_ALREADY_OVERRIDDEN_OBJC_INITIALIZER!>@OptIn(kotlinx.cinterop.BetaInteropApi::class)
|
||||
@OverrideInit
|
||||
constructor(coder: NSCoder) { }
|
||||
constructor(coder: NSCoder) { }<!>
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
override fun initWithCoder(coder: NSCoder): String? = "zzz"
|
||||
|
||||
Reference in New Issue
Block a user