Capitalize/decapitalize only ASCII characters across project

Use {de,}capitalizeAsciiOnly and to{Lower,Upper}CaseAsciiOnly where
possible, and stdlib's functions with Locale.US everywhere else.

Otherwise, if the default system locale is Turkish, the capital latin
letter "I" is transformed in toLowerCase to "ı" (see
https://github.com/JetBrains/kotlin/blob/66bc142f92085047a1ca64f9a291f0496e33dd98/libraries/stdlib/jvm/test/text/StringJVMTest.kt#L119),
which for example breaks the codegen for `intArrayOf` in
KT-25400/KT-43405.

Similarly, lower case latin letter "i" is transformed to "İ".

 #KT-13631 Fixed
 #KT-25400 Fixed
 #KT-43405 Fixed
This commit is contained in:
Alexander Udalov
2020-11-17 20:26:19 +01:00
parent 5d4b0b19d4
commit 77a9d14f93
53 changed files with 173 additions and 121 deletions
@@ -52,6 +52,7 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.isError
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeAsciiOnly
import org.jetbrains.kotlin.util.isJavaDescriptor
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
@@ -369,7 +370,7 @@ private class ConvertGettersAndSettersToPropertyStatefulProcessing(
return declarations
.asSequence()
.mapNotNull { it.asPropertyAccessor() }
.groupBy { it.name.removePrefix("is").decapitalize() }
.groupBy { it.name.removePrefix("is").decapitalizeAsciiOnly() }
.values
.mapNotNull { group ->
val realGetter = group.firstIsInstanceOrNull<RealGetter>()
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.nj2k.tree.JKLambdaExpression
import org.jetbrains.kotlin.nj2k.tree.JKParameter
import org.jetbrains.kotlin.nj2k.tree.JKTreeRoot
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.*
object ConversionsRunner {
private fun createConversions(context: NewJ2kConverterContext) = listOf(
@@ -105,7 +106,7 @@ object ConversionsRunner {
private fun Conversion.description(): String {
val conversionName = this::class.simpleName
val words = conversionName?.let { wordRegex.findAll(conversionName).map { it.value.decapitalize() }.toList() }
val words = conversionName?.let { wordRegex.findAll(conversionName).map { it.value.decapitalize(Locale.US) }.toList() }
return when {
conversionName == null -> "Converting..."
conversionName.endsWith("Conversion") -> "Converting ${words!!.dropLast(1).joinToString(" ")}"
@@ -114,4 +115,4 @@ object ConversionsRunner {
}
private val wordRegex = "[A-Z][a-z0-9]+".toRegex()
}
}
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.nj2k.conversions
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.tree.*
import org.jetbrains.kotlin.nj2k.types.primitiveTypes
import java.util.*
class BoxedTypeOperationsConversion(context: NewJ2kConverterContext) : RecursiveApplicableConversionBase(context) {
override fun applyToElement(element: JKTreeElement): JKTreeElement {
@@ -40,7 +40,7 @@ class BoxedTypeOperationsConversion(context: NewJ2kConverterContext) : Recursive
if (operationType !in primitiveTypeNames) return null
return JKCallExpressionImpl(
symbolProvider.provideMethodSymbol(
"kotlin.${primitiveTypeName.capitalize()}.to${operationType.capitalize()}"
"kotlin.${primitiveTypeName.capitalize(Locale.US)}.to${operationType.capitalize(Locale.US)}"
),
JKArgumentList()
).withFormattingFrom(methodCallExpression)
@@ -59,4 +59,4 @@ class BoxedTypeOperationsConversion(context: NewJ2kConverterContext) : Recursive
private val primitiveTypeUnwrapRegexp =
"""([\w.]+)\.(\w+)Value""".toRegex()
}
}
}
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.nj2k.types.*
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.*
class ImplicitCastsConversion(context: NewJ2kConverterContext) : RecursiveApplicableConversionBase(context) {
override fun applyToElement(element: JKTreeElement): JKTreeElement {
@@ -138,8 +139,8 @@ class ImplicitCastsConversion(context: NewJ2kConverterContext) : RecursiveApplic
}
}
val initialTypeName = expressionTypeAsPrimitive.jvmPrimitiveType.javaKeywordName.capitalize()
val conversionFunctionName = "to${toTypeAsPrimitive.jvmPrimitiveType.javaKeywordName.capitalize()}"
val initialTypeName = expressionTypeAsPrimitive.jvmPrimitiveType.javaKeywordName.capitalize(Locale.US)
val conversionFunctionName = "to${toTypeAsPrimitive.jvmPrimitiveType.javaKeywordName.capitalize(Locale.US)}"
return JKQualifiedExpression(
copyTreeAndDetach().parenthesizeIfBinaryExpression(),
JKCallExpressionImpl(
@@ -164,4 +165,4 @@ class ImplicitCastsConversion(context: NewJ2kConverterContext) : RecursiveApplic
val lastArrayType = realParameterTypes.lastOrNull()?.arrayInnerType() ?: return realParameterTypes
return realParameterTypes.subList(0, realParameterTypes.lastIndex) + lastArrayType
}
}
}
+4 -3
View File
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.nj2k
import org.jetbrains.kotlin.lexer.KtKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeAsciiOnly
fun <T> List<T>.replace(element: T, replacer: T): List<T> {
val mutableList = toMutableList()
@@ -22,14 +23,14 @@ fun String.asGetterName() =
?.takeIf {
it.isNotEmpty() && it.first().isUpperCase()
|| it.startsWith("is") && it.length > 2 && it[2].isUpperCase()
}?.decapitalize()
}?.decapitalizeAsciiOnly()
?.escaped()
fun String.asSetterName() =
takeIf { JvmAbi.isSetterName(it) }
?.removePrefix("set")
?.takeIf { it.isNotEmpty() && it.first().isUpperCase() }
?.decapitalize()
?.decapitalizeAsciiOnly()
?.escaped()
fun String.isPossiblyGetterOrSetterName() =
@@ -40,4 +41,4 @@ private val KEYWORDS = KtTokens.KEYWORDS.types.map { (it as KtKeywordToken).valu
fun String.escaped() =
if (this in KEYWORDS || '$' in this) "`$this`"
else this
else this