[FIR] Extract common utilities for ObjCName checkers
Previously there were cross-references between `FirNativeObjCNameChecker` and `FirNativeObjCNameOverridesChecker` checkers, which made the flow of code quite non-intuitive This commit extracts common methods and classes used by them into separate `FirNativeObjCNameUtilities` object
This commit is contained in:
committed by
Space Team
parent
72b0372927
commit
728f42d02a
+5
-51
@@ -5,12 +5,9 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.analysis.native.checkers
|
package org.jetbrains.kotlin.fir.analysis.native.checkers
|
||||||
|
|
||||||
import org.jetbrains.kotlin.builtins.StandardNames
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
import org.jetbrains.kotlin.fir.FirAnnotationContainer
|
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
||||||
@@ -22,30 +19,21 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INVA
|
|||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INVALID_OBJC_NAME_FIRST_CHAR
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INVALID_OBJC_NAME_FIRST_CHAR
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.MISSING_EXACT_OBJC_NAME
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.MISSING_EXACT_OBJC_NAME
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.NON_LITERAL_OBJC_NAME_ARG
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.NON_LITERAL_OBJC_NAME_ARG
|
||||||
import org.jetbrains.kotlin.fir.analysis.native.checkers.FirNativeObjCNameOverridesChecker.check
|
import org.jetbrains.kotlin.fir.analysis.native.checkers.FirNativeObjCNameUtilities.ObjCName
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.native.checkers.FirNativeObjCNameUtilities.checkCallableMember
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.native.checkers.FirNativeObjCNameUtilities.getObjCNames
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.isOverride
|
import org.jetbrains.kotlin.fir.declarations.utils.isOverride
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
|
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
|
||||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
|
|
||||||
object FirNativeObjCNameChecker : FirBasicDeclarationChecker() {
|
object FirNativeObjCNameChecker : FirBasicDeclarationChecker() {
|
||||||
|
|
||||||
private val objCNameClassId = ClassId.topLevel(FqName("kotlin.native.ObjCName"))
|
|
||||||
private val swiftNameName = Name.identifier("swiftName")
|
|
||||||
private val exactName = Name.identifier("exact")
|
|
||||||
|
|
||||||
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||||
checkDeclaration(declaration, context, reporter)
|
checkDeclaration(declaration, context, reporter)
|
||||||
if (declaration is FirCallableDeclaration && (declaration is FirSimpleFunction || declaration is FirProperty)) {
|
if (declaration is FirCallableDeclaration && (declaration is FirSimpleFunction || declaration is FirProperty)) {
|
||||||
val containingClass = context.containingDeclarations.lastOrNull() as? FirClass
|
val containingClass = context.containingDeclarations.lastOrNull() as? FirClass
|
||||||
if (containingClass != null) {
|
if (containingClass != null) {
|
||||||
val firTypeScope = containingClass.unsubstitutedScope(context)
|
val firTypeScope = containingClass.unsubstitutedScope(context)
|
||||||
check(firTypeScope, declaration.symbol, declaration, context, reporter)
|
checkCallableMember(firTypeScope, declaration.symbol, declaration, context, reporter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,39 +85,5 @@ object FirNativeObjCNameChecker : FirBasicDeclarationChecker() {
|
|||||||
reporter.reportOn(annotationSource, MISSING_EXACT_OBJC_NAME, context)
|
reporter.reportOn(annotationSource, MISSING_EXACT_OBJC_NAME, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ObjCName(
|
|
||||||
val annotation: FirAnnotation
|
|
||||||
) {
|
|
||||||
val name: String? = annotation.getStringArgument(StandardNames.NAME)
|
|
||||||
val swiftName: String? = annotation.getStringArgument(swiftNameName)
|
|
||||||
val exact: Boolean = annotation.getBooleanArgument(exactName) ?: false
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean =
|
|
||||||
other is ObjCName && name == other.name && swiftName == other.swiftName && exact == other.exact
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = name.hashCode()
|
|
||||||
result = 31 * result + swiftName.hashCode()
|
|
||||||
result = 31 * result + exact.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun FirAnnotationContainer.getObjCName(session: FirSession): ObjCName? =
|
|
||||||
getAnnotationByClassId(objCNameClassId, session)?.let(::ObjCName)
|
|
||||||
|
|
||||||
private fun FirBasedSymbol<*>.getObjCName(session: FirSession): ObjCName? =
|
|
||||||
getAnnotationByClassId(objCNameClassId, session)?.let(::ObjCName)
|
|
||||||
|
|
||||||
fun FirBasedSymbol<*>.getObjCNames(session: FirSession): List<ObjCName?> = when (this) {
|
|
||||||
is FirFunctionSymbol<*> -> buildList {
|
|
||||||
add((this@getObjCNames as FirBasedSymbol<*>).getObjCName(session))
|
|
||||||
add(resolvedReceiverTypeRef?.getObjCName(session))
|
|
||||||
add(receiverParameter?.getObjCName(session))
|
|
||||||
valueParameterSymbols.forEach { add(it.getObjCName(session)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> listOf(getObjCName(session))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-53
@@ -6,24 +6,14 @@
|
|||||||
package org.jetbrains.kotlin.fir.analysis.native.checkers
|
package org.jetbrains.kotlin.fir.analysis.native.checkers
|
||||||
|
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
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.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.INCOMPATIBLE_OBJC_NAME_OVERRIDE
|
import org.jetbrains.kotlin.fir.analysis.native.checkers.FirNativeObjCNameUtilities.checkCallableMember
|
||||||
import org.jetbrains.kotlin.fir.analysis.native.checkers.FirNativeObjCNameChecker.getObjCNames
|
|
||||||
import org.jetbrains.kotlin.fir.containingClassLookupTag
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.fullyExpandedClass
|
|
||||||
import org.jetbrains.kotlin.fir.isIntersectionOverride
|
import org.jetbrains.kotlin.fir.isIntersectionOverride
|
||||||
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
|
||||||
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
|
|
||||||
import org.jetbrains.kotlin.fir.scopes.processAllFunctions
|
import org.jetbrains.kotlin.fir.scopes.processAllFunctions
|
||||||
import org.jetbrains.kotlin.fir.scopes.processAllProperties
|
import org.jetbrains.kotlin.fir.scopes.processAllProperties
|
||||||
import org.jetbrains.kotlin.fir.scopes.retrieveDirectOverriddenOf
|
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
|
||||||
|
|
||||||
object FirNativeObjCNameOverridesChecker : FirClassChecker() {
|
object FirNativeObjCNameOverridesChecker : FirClassChecker() {
|
||||||
|
|
||||||
@@ -32,51 +22,11 @@ object FirNativeObjCNameOverridesChecker : FirClassChecker() {
|
|||||||
val firTypeScope = declaration.unsubstitutedScope(context)
|
val firTypeScope = declaration.unsubstitutedScope(context)
|
||||||
firTypeScope.processAllFunctions { symbol ->
|
firTypeScope.processAllFunctions { symbol ->
|
||||||
if (!symbol.isIntersectionOverride) return@processAllFunctions
|
if (!symbol.isIntersectionOverride) return@processAllFunctions
|
||||||
check(firTypeScope, symbol, declaration, context, reporter)
|
checkCallableMember(firTypeScope, symbol, declaration, context, reporter)
|
||||||
}
|
}
|
||||||
firTypeScope.processAllProperties { symbol ->
|
firTypeScope.processAllProperties { symbol ->
|
||||||
if (!symbol.isIntersectionOverride) return@processAllProperties
|
if (!symbol.isIntersectionOverride) return@processAllProperties
|
||||||
check(firTypeScope, symbol, declaration, context, reporter)
|
checkCallableMember(firTypeScope, symbol, declaration, context, reporter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun check(
|
|
||||||
firTypeScope: FirTypeScope,
|
|
||||||
memberSymbol: FirCallableSymbol<*>,
|
|
||||||
declarationToReport: FirDeclaration,
|
|
||||||
context: CheckerContext,
|
|
||||||
reporter: DiagnosticReporter
|
|
||||||
) {
|
|
||||||
val overriddenSymbols = firTypeScope.retrieveDirectOverriddenOf(memberSymbol)
|
|
||||||
if (overriddenSymbols.isEmpty()) return
|
|
||||||
val objCNames = overriddenSymbols.map { it.getFirstBaseSymbol(context).getObjCNames(context.session) }
|
|
||||||
if (!objCNames.allNamesEquals()) {
|
|
||||||
val containingDeclarations = overriddenSymbols.mapNotNull {
|
|
||||||
it.containingClassLookupTag()?.toFirRegularClassSymbol(context.session)
|
|
||||||
}
|
|
||||||
reporter.reportOn(
|
|
||||||
declarationToReport.source,
|
|
||||||
INCOMPATIBLE_OBJC_NAME_OVERRIDE,
|
|
||||||
declarationToReport.symbol,
|
|
||||||
containingDeclarations,
|
|
||||||
context
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun FirCallableSymbol<*>.getFirstBaseSymbol(context: CheckerContext): FirCallableSymbol<*> {
|
|
||||||
val session = context.session
|
|
||||||
val ownScope = containingClassLookupTag()?.toSymbol(session)?.fullyExpandedClass(session)?.unsubstitutedScope(context)
|
|
||||||
?: return this
|
|
||||||
val overriddenMemberSymbols = ownScope.retrieveDirectOverriddenOf(this)
|
|
||||||
return if (overriddenMemberSymbols.isEmpty()) this else overriddenMemberSymbols.first().getFirstBaseSymbol(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun List<List<FirNativeObjCNameChecker.ObjCName?>>.allNamesEquals(): Boolean {
|
|
||||||
val first = this[0]
|
|
||||||
for (i in 1 until size) {
|
|
||||||
if (first != this[i]) return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2024 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.builtins.StandardNames
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
|
import org.jetbrains.kotlin.fir.FirAnnotationContainer
|
||||||
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors
|
||||||
|
import org.jetbrains.kotlin.fir.containingClassLookupTag
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
|
||||||
|
import org.jetbrains.kotlin.fir.scopes.retrieveDirectOverriddenOf
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
|
object FirNativeObjCNameUtilities {
|
||||||
|
private val objCNameClassId = ClassId.topLevel(FqName("kotlin.native.ObjCName"))
|
||||||
|
private val swiftNameName = Name.identifier("swiftName")
|
||||||
|
private val exactName = Name.identifier("exact")
|
||||||
|
|
||||||
|
fun FirBasedSymbol<*>.getObjCNames(session: FirSession): List<ObjCName?> = when (this) {
|
||||||
|
is FirFunctionSymbol<*> -> buildList {
|
||||||
|
add((this@getObjCNames as FirBasedSymbol<*>).getObjCName(session))
|
||||||
|
add(resolvedReceiverTypeRef?.getObjCName(session))
|
||||||
|
add(receiverParameter?.getObjCName(session))
|
||||||
|
valueParameterSymbols.forEach { add(it.getObjCName(session)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> listOf(getObjCName(session))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FirAnnotationContainer.getObjCName(session: FirSession): ObjCName? =
|
||||||
|
getAnnotationByClassId(objCNameClassId, session)?.let(::ObjCName)
|
||||||
|
|
||||||
|
private fun FirBasedSymbol<*>.getObjCName(session: FirSession): ObjCName? =
|
||||||
|
getAnnotationByClassId(objCNameClassId, session)?.let(::ObjCName)
|
||||||
|
|
||||||
|
class ObjCName(
|
||||||
|
val annotation: FirAnnotation
|
||||||
|
) {
|
||||||
|
val name: String? = annotation.getStringArgument(StandardNames.NAME)
|
||||||
|
val swiftName: String? = annotation.getStringArgument(swiftNameName)
|
||||||
|
val exact: Boolean = annotation.getBooleanArgument(exactName) ?: false
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean =
|
||||||
|
other is ObjCName && name == other.name && swiftName == other.swiftName && exact == other.exact
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = name.hashCode()
|
||||||
|
result = 31 * result + swiftName.hashCode()
|
||||||
|
result = 31 * result + exact.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkCallableMember(
|
||||||
|
firTypeScope: FirTypeScope,
|
||||||
|
memberSymbol: FirCallableSymbol<*>,
|
||||||
|
declarationToReport: FirDeclaration,
|
||||||
|
context: CheckerContext,
|
||||||
|
reporter: DiagnosticReporter
|
||||||
|
) {
|
||||||
|
val overriddenSymbols = firTypeScope.retrieveDirectOverriddenOf(memberSymbol)
|
||||||
|
if (overriddenSymbols.isEmpty()) return
|
||||||
|
val objCNames = overriddenSymbols.map { it.getFirstBaseSymbol(context).getObjCNames(context.session) }
|
||||||
|
if (!objCNames.allNamesEquals()) {
|
||||||
|
val containingDeclarations = overriddenSymbols.mapNotNull {
|
||||||
|
it.containingClassLookupTag()?.toFirRegularClassSymbol(context.session)
|
||||||
|
}
|
||||||
|
reporter.reportOn(
|
||||||
|
declarationToReport.source,
|
||||||
|
FirNativeErrors.INCOMPATIBLE_OBJC_NAME_OVERRIDE,
|
||||||
|
declarationToReport.symbol,
|
||||||
|
containingDeclarations,
|
||||||
|
context
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FirCallableSymbol<*>.getFirstBaseSymbol(context: CheckerContext): FirCallableSymbol<*> {
|
||||||
|
val session = context.session
|
||||||
|
val ownScope = containingClassLookupTag()?.toSymbol(session)?.fullyExpandedClass(session)?.unsubstitutedScope(context)
|
||||||
|
?: return this
|
||||||
|
val overriddenMemberSymbols = ownScope.retrieveDirectOverriddenOf(this)
|
||||||
|
return if (overriddenMemberSymbols.isEmpty()) this else overriddenMemberSymbols.first().getFirstBaseSymbol(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<List<ObjCName?>>.allNamesEquals(): Boolean {
|
||||||
|
val first = this[0]
|
||||||
|
for (i in 1 until size) {
|
||||||
|
if (first != this[i]) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user