[K/N] Prohibit empty ObjCNames

This commit is contained in:
Rick Clephas
2022-09-03 18:56:00 +02:00
committed by SvyatoslavScherbina
parent 0290f09ac2
commit b406722e20
11 changed files with 64 additions and 24 deletions
@@ -55,6 +55,7 @@ object NATIVE_DIAGNOSTICS_LIST : DiagnosticList("FirNativeErrors") {
val INVALID_OBJC_NAME_FIRST_CHAR by error<KtElement> {
parameter<String>("characters")
}
val EMPTY_OBJC_NAME by error<KtElement>()
val INCOMPATIBLE_OBJC_NAME_OVERRIDE by error<KtElement> {
parameter<FirBasedSymbol<*>>("declaration")
parameter<Collection<FirRegularClassSymbol>>("containingClasses")
@@ -40,6 +40,7 @@ object FirNativeErrors {
val INVALID_OBJC_NAME by error0<KtElement>()
val INVALID_OBJC_NAME_CHARS by error1<KtElement, String>()
val INVALID_OBJC_NAME_FIRST_CHAR by error1<KtElement, String>()
val EMPTY_OBJC_NAME by error0<KtElement>()
val INCOMPATIBLE_OBJC_NAME_OVERRIDE by error2<KtElement, FirBasedSymbol<*>, Collection<FirRegularClassSymbol>>()
val INAPPLICABLE_EXACT_OBJC_NAME by error0<KtElement>()
val MISSING_EXACT_OBJC_NAME by error0<KtElement>()
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
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.checkMissingMessages
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.EMPTY_OBJC_NAME
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
@@ -65,6 +66,7 @@ object FirNativeErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
)
map.put(INAPPLICABLE_OBJC_NAME, "@ObjCName is not applicable on overrides")
map.put(INVALID_OBJC_NAME, "@ObjCName should have a name and/or swiftName")
map.put(EMPTY_OBJC_NAME, "Empty @ObjCName names aren't supported")
map.put(INVALID_OBJC_NAME_CHARS, "@ObjCName contains illegal characters: {0}", TO_STRING)
map.put(INVALID_OBJC_NAME_FIRST_CHAR, "@ObjCName contains illegal first characters: {0}", TO_STRING)
map.put(INCOMPATIBLE_OBJC_NAME_OVERRIDE, "Member \"{0}\" inherits inconsistent @ObjCName from {1}", SYMBOL, SYMBOLS)
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.FirAnnotationContainer
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.unsubstitutedScope
import org.jetbrains.kotlin.fir.analysis.diagnostics.native.FirNativeErrors.EMPTY_OBJC_NAME
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.INVALID_OBJC_NAME
@@ -79,6 +80,9 @@ object FirNativeObjCNameChecker : FirBasicDeclarationChecker() {
if (invalidFirstChars.isNotEmpty()) {
reporter.reportOn(annotationSource, INVALID_OBJC_NAME_FIRST_CHAR, invalidFirstChars.joinToString(""), context)
}
if (objCName.name?.isEmpty() == true || objCName.swiftName?.isEmpty() == true) {
reporter.reportOn(annotationSource, EMPTY_OBJC_NAME, context)
}
val invalidNameChars = objCName.name?.toSet()?.subtract(validChars) ?: emptySet()
val invalidSwiftNameChars = objCName.swiftName?.toSet()?.subtract(validChars) ?: emptySet()
val invalidChars = invalidNameChars + invalidSwiftNameChars
@@ -96,8 +100,8 @@ object FirNativeObjCNameChecker : FirBasicDeclarationChecker() {
class ObjCName(
val annotation: FirAnnotation
) {
val name: String? = annotation.getStringArgument(nameName)?.takeIf { it.isNotBlank() }
val swiftName: String? = annotation.getStringArgument(swiftNameName)?.takeIf { it.isNotBlank() }
val name: String? = annotation.getStringArgument(nameName)
val swiftName: String? = annotation.getStringArgument(swiftNameName)
val exact: Boolean = annotation.getBooleanArgument(exactName) ?: false
override fun equals(other: Any?): Boolean =
+19 -7
View File
@@ -33,16 +33,22 @@ class KotlinSubClass: KotlinClass() {
}
<!INVALID_OBJC_NAME!>@ObjCName()<!>
val invalidObjCNameA: Int = 0
val invalidObjCName: Int = 0
<!INVALID_OBJC_NAME!>@ObjCName("", "")<!>
val invalidObjCNameB: Int = 0
<!EMPTY_OBJC_NAME!>@ObjCName("", "")<!>
val emptyObjCNameA: Int = 0
@ObjCName("validName", "")
val validBlankObjCNameA: Int = 0
<!EMPTY_OBJC_NAME!>@ObjCName("validName", "")<!>
val emptyObjCNameB: Int = 0
@ObjCName("", "validName")
val validBlankObjCNameB: Int = 0
<!EMPTY_OBJC_NAME!>@ObjCName("", "validName")<!>
val emptyObjCNameC: Int = 0
@ObjCName("validName")
val validObjCNameA: Int = 0
@ObjCName(swiftName = "validName")
val validObjCNameB: Int = 0
<!INVALID_OBJC_NAME_CHARS!>@ObjCName("validName", "invalid.name")<!>
val invalidCharactersObjCNameA: Int = 0
@@ -56,6 +62,12 @@ val invalidFirstCharacterObjCNameA: Int = 0
<!INVALID_OBJC_NAME_FIRST_CHAR!>@ObjCName("1validName", "validName1")<!>
val invalidFirstCharacterObjCNameB: Int = 0
<!INVALID_OBJC_NAME_CHARS, INVALID_OBJC_NAME_FIRST_CHAR!>@ObjCName("validName", " ")<!>
val blankObjCNameA: Int = 0
<!INVALID_OBJC_NAME_CHARS, INVALID_OBJC_NAME_FIRST_CHAR!>@ObjCName(" ", "validName")<!>
val blankObjCNameB: Int = 0
<!MISSING_EXACT_OBJC_NAME!>@ObjCName(swiftName = "SwiftMissingExactName", exact = true)<!>
class MissingExactName
+19 -7
View File
@@ -33,16 +33,22 @@ class KotlinSubClass: KotlinClass() {
}
<!INVALID_OBJC_NAME!>@ObjCName()<!>
val invalidObjCNameA: Int = 0
val invalidObjCName: Int = 0
<!INVALID_OBJC_NAME!>@ObjCName("", "")<!>
val invalidObjCNameB: Int = 0
<!EMPTY_OBJC_NAME!>@ObjCName("", "")<!>
val emptyObjCNameA: Int = 0
@ObjCName("validName", "")
val validBlankObjCNameA: Int = 0
<!EMPTY_OBJC_NAME!>@ObjCName("validName", "")<!>
val emptyObjCNameB: Int = 0
@ObjCName("", "validName")
val validBlankObjCNameB: Int = 0
<!EMPTY_OBJC_NAME!>@ObjCName("", "validName")<!>
val emptyObjCNameC: Int = 0
@ObjCName("validName")
val validObjCNameA: Int = 0
@ObjCName(swiftName = "validName")
val validObjCNameB: Int = 0
<!INVALID_OBJC_NAME_CHARS!>@ObjCName("validName", "invalid.name")<!>
val invalidCharactersObjCNameA: Int = 0
@@ -56,6 +62,12 @@ val invalidFirstCharacterObjCNameA: Int = 0
<!INVALID_OBJC_NAME_FIRST_CHAR!>@ObjCName("1validName", "validName1")<!>
val invalidFirstCharacterObjCNameB: Int = 0
<!INVALID_OBJC_NAME_CHARS, INVALID_OBJC_NAME_FIRST_CHAR!>@ObjCName("validName", " ")<!>
val blankObjCNameA: Int = 0
<!INVALID_OBJC_NAME_CHARS, INVALID_OBJC_NAME_FIRST_CHAR!>@ObjCName(" ", "validName")<!>
val blankObjCNameB: Int = 0
<!MISSING_EXACT_OBJC_NAME!>@ObjCName(swiftName = "SwiftMissingExactName", exact = true)<!>
class MissingExactName
+8 -5
View File
@@ -1,17 +1,21 @@
package
@kotlin.native.ObjCName(name = "validName", swiftName = " ") public val blankObjCNameA: kotlin.Int = 0
@kotlin.native.ObjCName(name = " ", swiftName = "validName") public val blankObjCNameB: kotlin.Int = 0
@kotlin.native.ObjCName(name = "", swiftName = "") public val emptyObjCNameA: kotlin.Int = 0
@kotlin.native.ObjCName(name = "validName", swiftName = "") public val emptyObjCNameB: kotlin.Int = 0
@kotlin.native.ObjCName(name = "", swiftName = "validName") public val emptyObjCNameC: kotlin.Int = 0
private const val exact: kotlin.Boolean = false
@kotlin.native.ObjCName(exact = "not a boolean", name = "invalidArgsObjC", swiftName = false) public val invalidArgs: kotlin.Int = 0
@kotlin.native.ObjCName(name = "validName", swiftName = "invalid.name") public val invalidCharactersObjCNameA: kotlin.Int = 0
@kotlin.native.ObjCName(name = "invalid.name", swiftName = "validName") public val invalidCharactersObjCNameB: kotlin.Int = 0
@kotlin.native.ObjCName(name = "validName1", swiftName = "1validName") public val invalidFirstCharacterObjCNameA: kotlin.Int = 0
@kotlin.native.ObjCName(name = "1validName", swiftName = "validName1") public val invalidFirstCharacterObjCNameB: kotlin.Int = 0
@kotlin.native.ObjCName public val invalidObjCNameA: kotlin.Int = 0
@kotlin.native.ObjCName(name = "", swiftName = "") public val invalidObjCNameB: kotlin.Int = 0
@kotlin.native.ObjCName public val invalidObjCName: kotlin.Int = 0
@kotlin.native.ObjCName(exact = false, name = "nonLiteralArgsObjC", swiftName = "nonLiteralArgsSwift") public val nonLiteralArgs: kotlin.Int = 0
private const val objcName: kotlin.String = "nonLiteralArgsObjC"
@kotlin.native.ObjCName(name = "validName", swiftName = "") public val validBlankObjCNameA: kotlin.Int = 0
@kotlin.native.ObjCName(name = "", swiftName = "validName") public val validBlankObjCNameB: kotlin.Int = 0
@kotlin.native.ObjCName(name = "validName") public val validObjCNameA: kotlin.Int = 0
@kotlin.native.ObjCName(swiftName = "validName") public val validObjCNameB: kotlin.Int = 0
public open class Base {
public constructor Base()
@@ -164,4 +168,3 @@ package kotlin {
}
}
}
@@ -30,7 +30,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor
import org.jetbrains.kotlin.resolve.source.PsiSourceFile
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal interface ObjCExportNameTranslator {
fun getFileClassName(file: KtFile): ObjCExportNamer.ClassOrProtocolName
@@ -46,6 +46,7 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
ErrorsNative.INVALID_OBJC_NAME_FIRST_CHAR, "@ObjCName contains illegal first characters: {0}",
CommonRenderers.STRING
)
put(ErrorsNative.EMPTY_OBJC_NAME, "Empty @ObjCName names aren't supported")
put(
ErrorsNative.INCOMPATIBLE_OBJC_NAME_OVERRIDE, "Member \"{0}\" inherits inconsistent @ObjCName from {1}",
Renderers.NAME,
@@ -45,6 +45,8 @@ object ErrorsNative {
@JvmField
val INVALID_OBJC_NAME_FIRST_CHAR = DiagnosticFactory1.create<KtElement, String>(Severity.ERROR)
@JvmField
val EMPTY_OBJC_NAME = DiagnosticFactory0.create<KtElement>(Severity.ERROR)
@JvmField
val INCOMPATIBLE_OBJC_NAME_OVERRIDE = DiagnosticFactory2.create<KtElement, DeclarationDescriptor, Collection<DeclarationDescriptor>>(Severity.ERROR)
@JvmField
val INAPPLICABLE_EXACT_OBJC_NAME = DiagnosticFactory0.create<KtElement>(Severity.ERROR)
@@ -64,6 +64,9 @@ object NativeObjCNameChecker : DeclarationChecker {
if (objCName.name == null && objCName.swiftName == null) {
context.trace.report(ErrorsNative.INVALID_OBJC_NAME.on(reportLocation))
}
if (objCName.name?.isEmpty() == true || objCName.swiftName?.isEmpty() == true) {
context.trace.report(ErrorsNative.EMPTY_OBJC_NAME.on(reportLocation))
}
val invalidNameFirstChar = objCName.name?.firstOrNull()?.takeUnless(validFirstChars::contains)
val invalidSwiftNameFirstChar = objCName.swiftName?.firstOrNull()?.takeUnless(validFirstChars::contains)
val invalidFirstChars = setOfNotNull(invalidNameFirstChar, invalidSwiftNameFirstChar)
@@ -87,8 +90,8 @@ object NativeObjCNameChecker : DeclarationChecker {
class ObjCName(
val annotation: AnnotationDescriptor
) {
val name: String? = annotation.argumentValue("name")?.value?.safeAs<String>()?.takeIf { it.isNotBlank() }
val swiftName: String? = annotation.argumentValue("swiftName")?.value?.safeAs<String>()?.takeIf { it.isNotBlank() }
val name: String? = annotation.argumentValue("name")?.value?.safeAs<String>()
val swiftName: String? = annotation.argumentValue("swiftName")?.value?.safeAs<String>()
val exact: Boolean = annotation.argumentValue("exact")?.value?.safeAs<Boolean>() ?: false
override fun equals(other: Any?): Boolean =