Native: report a warning on invalid characters in identifiers

This will become an error in Kotlin 1.7.

^KT-46182 Fixed
This commit is contained in:
Svyatoslav Scherbina
2021-08-31 08:50:14 +00:00
committed by Space
parent 64dc19fafb
commit d4a8c51637
8 changed files with 260 additions and 8 deletions
@@ -41,6 +41,7 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
"@ThreadLocal is applicable only to property with backing field, to property with delegation or to objects"
)
put(ErrorsNative.INAPPLICABLE_THREAD_LOCAL_TOP_LEVEL, "@ThreadLocal is applicable only to top level declarations")
put(ErrorsNative.INVALID_CHARACTERS_NATIVE, "Name {0}", Renderers.STRING);
}
}
@@ -5,11 +5,10 @@
package org.jetbrains.kotlin.resolve.konan.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
@@ -35,6 +34,9 @@ object ErrorsNative {
val INAPPLICABLE_THREAD_LOCAL_TOP_LEVEL = DiagnosticFactory0.create<KtElement>(Severity.ERROR)
@JvmField
val VARIABLE_IN_ENUM = DiagnosticFactory0.create<KtElement>(Severity.WARNING)
@JvmField
val INVALID_CHARACTERS_NATIVE = DiagnosticFactoryForDeprecation1.create<PsiElement, String>(LanguageFeature.ProhibitInvalidCharsInNativeIdentifiers)
init {
Errors.Initializer.initializeFactoryNames(ErrorsNative::class.java)
}
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2021 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.resolve.konan.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.IdentifierChecker
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
// Note: IdentifierChecker doesn't check typealiases, so inheriting DeclarationChecker as well.
// Originally based on JvmSimpleNameBacktickChecker.
class NativeIdentifierChecker(private val languageVersionSettings: LanguageVersionSettings) : IdentifierChecker, DeclarationChecker {
// Also includes characters used by IR mangler (see MangleConstant).
private val invalidChars = setOf(
'.', ';', ',', '(', ')', '[', ']', '{', '}', '/', '<', '>',
':', '\\', '$', '&', '~', '*', '?', '#', '|', '§', '%', '@',
)
override fun checkIdentifier(simpleNameExpression: KtSimpleNameExpression, diagnosticHolder: DiagnosticSink) {
reportIfNeeded(simpleNameExpression.getReferencedName(), { simpleNameExpression.getIdentifier() }, diagnosticHolder)
}
override fun checkDeclaration(declaration: KtDeclaration, diagnosticHolder: DiagnosticSink) {
if (declaration is KtDestructuringDeclaration) {
declaration.entries.forEach { checkNamed(it, diagnosticHolder) }
}
if (declaration is KtCallableDeclaration) {
declaration.valueParameters.forEach { checkNamed(it, diagnosticHolder) }
}
if (declaration is KtTypeParameterListOwner) {
declaration.typeParameters.forEach { checkNamed(it, diagnosticHolder) }
}
if (declaration is KtNamedDeclaration) {
checkNamed(declaration, diagnosticHolder)
}
}
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (declaration is KtTypeAlias) {
checkNamed(declaration, context.trace)
}
}
private fun checkNamed(declaration: KtNamedDeclaration, diagnosticHolder: DiagnosticSink) {
val name = declaration.name ?: return
reportIfNeeded(name, { declaration.nameIdentifier ?: declaration }, diagnosticHolder)
}
private fun reportIfNeeded(name: String, reportOn: () -> PsiElement?, diagnosticHolder: DiagnosticSink) {
val text = KtPsiUtil.unquoteIdentifier(name)
when {
text.isEmpty() -> {
diagnosticHolder.report(
ErrorsNative.INVALID_CHARACTERS_NATIVE.on(
languageVersionSettings,
reportOn() ?: return,
"should not be empty"
)
)
}
text.any { it in invalidChars } -> {
diagnosticHolder.report(
ErrorsNative.INVALID_CHARACTERS_NATIVE.on(
languageVersionSettings,
reportOn() ?: return,
"contains illegal characters: " +
invalidChars.intersect(text.toSet()).joinToString("", prefix = "\"", postfix = "\"")
)
)
}
}
}
}
@@ -18,10 +18,7 @@ import org.jetbrains.kotlin.resolve.calls.checkers.TypeOfChecker
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
import org.jetbrains.kotlin.resolve.inline.ReasonableInlineRule
import org.jetbrains.kotlin.resolve.jvm.checkers.SuperCallWithDefaultArgumentsChecker
import org.jetbrains.kotlin.resolve.konan.diagnostics.NativeSharedImmutableChecker
import org.jetbrains.kotlin.resolve.konan.diagnostics.NativeThreadLocalChecker
import org.jetbrains.kotlin.resolve.konan.diagnostics.NativeThrowsChecker
import org.jetbrains.kotlin.resolve.konan.diagnostics.NativeTopLevelSingletonChecker
import org.jetbrains.kotlin.resolve.konan.diagnostics.*
object NativePlatformConfigurator : PlatformConfiguratorBase(
additionalCallCheckers = listOf(
@@ -35,6 +32,7 @@ object NativePlatformConfigurator : PlatformConfiguratorBase(
) {
override fun configureModuleComponents(container: StorageComponentContainer, languageVersionSettings: LanguageVersionSettings) {
container.useInstance(NativeInliningRule)
container.useImpl<NativeIdentifierChecker>()
}
override fun configureModuleDependentCheckers(container: StorageComponentContainer) {