feat(Escaped Identifiers): add ability to use any symbol wrapped in back ticks.
This commit is contained in:
@@ -19,6 +19,8 @@ package org.jetbrains.kotlin.js.naming
|
||||
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
|
||||
import org.jetbrains.kotlin.js.common.isES5IdentifierPart
|
||||
import org.jetbrains.kotlin.js.common.isES5IdentifierStart
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.getNameForAnnotatedObject
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isNativeObject
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -358,55 +360,4 @@ class NameSuggestion {
|
||||
return first.toString() + name.drop(1).map { if (it.isES5IdentifierPart()) it else '_' }.joinToString("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Char.isAllowedLatinLetterOrSpecial(): Boolean {
|
||||
return this in 'a'..'z' || this in 'A'..'Z' || this == '_' || this == '$'
|
||||
}
|
||||
|
||||
private fun Char.isAllowedSimpleDigit() =
|
||||
this in '0'..'9'
|
||||
|
||||
private fun Char.isNotAllowedSimpleCharacter() = when (this) {
|
||||
' ', '<', '>', '-', '?' -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
fun Char.isES5IdentifierStart(): Boolean {
|
||||
if (isAllowedLatinLetterOrSpecial()) return true
|
||||
if (isNotAllowedSimpleCharacter()) return false
|
||||
|
||||
return isES5IdentifierStartFull()
|
||||
}
|
||||
|
||||
// See ES 5.1 spec: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6
|
||||
private fun Char.isES5IdentifierStartFull() =
|
||||
Character.isLetter(this) || // Lu | Ll | Lt | Lm | Lo
|
||||
// Nl which is missing in Character.isLetter, but present in UnicodeLetter in spec
|
||||
Character.getType(this).toByte() == Character.LETTER_NUMBER
|
||||
|
||||
|
||||
fun Char.isES5IdentifierPart(): Boolean {
|
||||
if (isAllowedLatinLetterOrSpecial()) return true
|
||||
if (isAllowedSimpleDigit()) return true
|
||||
if (isNotAllowedSimpleCharacter()) return false
|
||||
|
||||
return isES5IdentifierStartFull() ||
|
||||
when (Character.getType(this).toByte()) {
|
||||
Character.NON_SPACING_MARK,
|
||||
Character.COMBINING_SPACING_MARK,
|
||||
Character.DECIMAL_DIGIT_NUMBER,
|
||||
Character.CONNECTOR_PUNCTUATION -> true
|
||||
else -> false
|
||||
} ||
|
||||
this == '\u200C' || // Zero-width non-joiner
|
||||
this == '\u200D' // Zero-width joiner
|
||||
}
|
||||
|
||||
fun String.isValidES5Identifier(): Boolean {
|
||||
if (isEmpty() || !this[0].isES5IdentifierStart()) return false
|
||||
for (idx in 1 ..length-1) {
|
||||
if (!get(idx).isES5IdentifierPart()) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -17,28 +17,28 @@ import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
|
||||
import org.jetbrains.kotlin.types.DynamicTypesAllowed
|
||||
|
||||
object JsPlatformConfigurator : PlatformConfiguratorBase(
|
||||
DynamicTypesAllowed(),
|
||||
additionalDeclarationCheckers = listOf(
|
||||
NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(),
|
||||
JsNameChecker, JsModuleChecker, JsExternalFileChecker,
|
||||
JsExternalChecker, JsInheritanceChecker, JsMultipleInheritanceChecker,
|
||||
JsRuntimeAnnotationChecker,
|
||||
JsDynamicDeclarationChecker,
|
||||
JsExportAnnotationChecker,
|
||||
JsExportDeclarationChecker
|
||||
),
|
||||
additionalCallCheckers = listOf(
|
||||
JsModuleCallChecker,
|
||||
JsDynamicCallChecker,
|
||||
JsDefinedExternallyCallChecker,
|
||||
),
|
||||
identifierChecker = JsIdentifierChecker
|
||||
DynamicTypesAllowed(),
|
||||
additionalDeclarationCheckers = listOf(
|
||||
NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(),
|
||||
JsNameChecker, JsModuleChecker, JsExternalFileChecker,
|
||||
JsExternalChecker, JsInheritanceChecker, JsMultipleInheritanceChecker,
|
||||
JsRuntimeAnnotationChecker,
|
||||
JsDynamicDeclarationChecker,
|
||||
JsExportAnnotationChecker,
|
||||
JsExportDeclarationChecker
|
||||
),
|
||||
additionalCallCheckers = listOf(
|
||||
JsModuleCallChecker,
|
||||
JsDynamicCallChecker,
|
||||
JsDefinedExternallyCallChecker,
|
||||
),
|
||||
) {
|
||||
override fun configureModuleComponents(container: StorageComponentContainer) {
|
||||
container.useInstance(NameSuggestion())
|
||||
container.useImpl<JsCallChecker>()
|
||||
container.useImpl<JsTypeSpecificityComparator>()
|
||||
container.useImpl<JsNameClashChecker>()
|
||||
container.useImpl<JsIdentifierChecker>()
|
||||
container.useImpl<JsNameCharsChecker>()
|
||||
container.useImpl<JsBuiltinNameClashChecker>()
|
||||
container.useInstance(JsModuleClassLiteralChecker)
|
||||
|
||||
+4
-1
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.js.resolve.diagnostics
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.js.naming.NameSuggestion
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -82,7 +83,9 @@ object JsDynamicCallChecker : CallChecker {
|
||||
}
|
||||
|
||||
private fun checkIdentifier(name: String?, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
if (name == null) return
|
||||
if (name == null || context.languageVersionSettings.supportsFeature(LanguageFeature.JsAllowInvalidCharsIdentifiersEscaping)) {
|
||||
return
|
||||
}
|
||||
if (NameSuggestion.sanitizeName(name) != name) {
|
||||
context.trace.report(ErrorsJs.NAME_CONTAINS_ILLEGAL_CHARS.on(reportOn))
|
||||
}
|
||||
|
||||
+6
-1
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.js.resolve.diagnostics
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.js.naming.NameSuggestion
|
||||
@@ -23,8 +25,11 @@ import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.resolve.IdentifierChecker
|
||||
|
||||
object JsIdentifierChecker : IdentifierChecker {
|
||||
class JsIdentifierChecker(private val languageVersionSettings: LanguageVersionSettings) : IdentifierChecker {
|
||||
override fun checkIdentifier(simpleNameExpression: KtSimpleNameExpression, diagnosticHolder: DiagnosticSink) {
|
||||
if (languageVersionSettings.supportsFeature(LanguageFeature.JsAllowInvalidCharsIdentifiersEscaping)) {
|
||||
return
|
||||
}
|
||||
val simpleName = simpleNameExpression.getReferencedName()
|
||||
|
||||
val hasIllegalChars = simpleName.split('.').any { NameSuggestion.sanitizeName(it) != it }
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.js.resolve.diagnostics
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
|
||||
@@ -16,6 +17,9 @@ import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||
|
||||
class JsNameCharsChecker(private val suggestion: NameSuggestion) : DeclarationChecker {
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
if (context.languageVersionSettings.supportsFeature(LanguageFeature.JsAllowInvalidCharsIdentifiersEscaping)) {
|
||||
return
|
||||
}
|
||||
val bindingContext = context.trace.bindingContext
|
||||
|
||||
if (descriptor is PropertyAccessorDescriptor && AnnotationsUtils.getJsName(descriptor) == null) return
|
||||
|
||||
Reference in New Issue
Block a user