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:
committed by
Space
parent
64dc19fafb
commit
d4a8c51637
@@ -0,0 +1,68 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -MISSING_DEPENDENCY_SUPERCLASS
|
||||
|
||||
// FIXME: rename identifiers.kt
|
||||
|
||||
// FILE: 1.kt
|
||||
package <!INVALID_CHARACTERS_NATIVE_WARNING!>`check.pkg`<!>
|
||||
|
||||
// FILE: 2.kt
|
||||
package totally.normal.pkg
|
||||
|
||||
class <!INVALID_CHARACTERS_NATIVE_WARNING!>`Check.Class`<!>
|
||||
class NormalClass {
|
||||
fun <!INVALID_CHARACTERS_NATIVE_WARNING!>`check$member`<!>() {}
|
||||
}
|
||||
|
||||
object <!INVALID_CHARACTERS_NATIVE_WARNING!>`Check;Object`<!>
|
||||
object NormalObject
|
||||
|
||||
data class Pair(val first: Int, val <!INVALID_CHARACTERS_NATIVE_WARNING!>`next,one`<!>: Int)
|
||||
|
||||
object Delegate {
|
||||
operator fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): Any? = null
|
||||
}
|
||||
|
||||
fun <!INVALID_CHARACTERS_NATIVE_WARNING!>`check(function`<!>() {
|
||||
val <!INVALID_CHARACTERS_NATIVE_WARNING!>`check)variable`<!> = 1
|
||||
val <!INVALID_CHARACTERS_NATIVE_WARNING!>`check[delegated[variable`<!> by Delegate
|
||||
|
||||
val normalVariable = 2
|
||||
val normalDelegatedVariable by Delegate
|
||||
|
||||
val (check, <!INVALID_CHARACTERS_NATIVE_WARNING!>`destructuring]declaration`<!>) = Pair(1, 2)
|
||||
}
|
||||
|
||||
fun normalFunction() {}
|
||||
|
||||
val <!INVALID_CHARACTERS_NATIVE_WARNING!>`check{property`<!> = 1
|
||||
val <!INVALID_CHARACTERS_NATIVE_WARNING!>`check}delegated}property`<!> by Delegate
|
||||
val normalProperty = 2
|
||||
val normalDelegatedProperty by Delegate
|
||||
|
||||
fun checkValueParameter(<!INVALID_CHARACTERS_NATIVE_WARNING!>`check/parameter`<!>: Int) {}
|
||||
|
||||
fun <<!INVALID_CHARACTERS_NATIVE_WARNING!>`check<type<parameter`<!>, normalTypeParameter> checkTypeParameter() {}
|
||||
|
||||
enum class <!INVALID_CHARACTERS_NATIVE_WARNING!>`Check>Enum>Entry`<!> {
|
||||
<!INVALID_CHARACTERS_NATIVE_WARNING!>`CHECK:ENUM:ENTRY`<!>;
|
||||
}
|
||||
|
||||
typealias <!INVALID_CHARACTERS_NATIVE_WARNING!>`check\typealias`<!> = Any
|
||||
|
||||
fun <!INVALID_CHARACTERS_NATIVE_WARNING!>`check&`<!>() {}
|
||||
|
||||
fun <!INVALID_CHARACTERS_NATIVE_WARNING!>`check~`<!>() {}
|
||||
|
||||
fun <!INVALID_CHARACTERS_NATIVE_WARNING!>`check*`<!>() {}
|
||||
|
||||
fun <!INVALID_CHARACTERS_NATIVE_WARNING!>`check?`<!>() {}
|
||||
|
||||
fun <!INVALID_CHARACTERS_NATIVE_WARNING!>`check#`<!>() {}
|
||||
|
||||
fun <!INVALID_CHARACTERS_NATIVE_WARNING!>`check|`<!>() {}
|
||||
|
||||
fun <!INVALID_CHARACTERS_NATIVE_WARNING!>`check§`<!>() {}
|
||||
|
||||
fun <!INVALID_CHARACTERS_NATIVE_WARNING!>`check%`<!>() {}
|
||||
|
||||
fun <!INVALID_CHARACTERS_NATIVE_WARNING!>`check@`<!>() {}
|
||||
@@ -0,0 +1,95 @@
|
||||
package
|
||||
|
||||
package totally {
|
||||
|
||||
package totally.normal {
|
||||
|
||||
package totally.normal.pkg {
|
||||
public val `check{property`: kotlin.Int = 1
|
||||
public val `check}delegated}property`: kotlin.Any?
|
||||
public val normalDelegatedProperty: kotlin.Any?
|
||||
public val normalProperty: kotlin.Int = 2
|
||||
public fun `check#`(): kotlin.Unit
|
||||
public fun `check%`(): kotlin.Unit
|
||||
public fun `check&`(): kotlin.Unit
|
||||
public fun `check(function`(): kotlin.Unit
|
||||
public fun `check*`(): kotlin.Unit
|
||||
public fun `check?`(): kotlin.Unit
|
||||
public fun `check@`(): kotlin.Unit
|
||||
public fun </*0*/ `check<type<parameter`, /*1*/ normalTypeParameter> checkTypeParameter(): kotlin.Unit
|
||||
public fun checkValueParameter(/*0*/ `check/parameter`: kotlin.Int): kotlin.Unit
|
||||
public fun `check|`(): kotlin.Unit
|
||||
public fun `check~`(): kotlin.Unit
|
||||
public fun `check§`(): kotlin.Unit
|
||||
public fun normalFunction(): kotlin.Unit
|
||||
|
||||
public final class `Check.Class` {
|
||||
public constructor `Check.Class`()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public object `Check;Object` {
|
||||
private constructor `Check;Object`()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final enum class `Check>Enum>Entry` : kotlin.Enum<totally.normal.pkg.`Check>Enum>Entry`> {
|
||||
enum entry `CHECK:ENUM:ENTRY`
|
||||
|
||||
private constructor `Check>Enum>Entry`()
|
||||
public final override /*1*/ /*fake_override*/ val name: kotlin.String
|
||||
public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int
|
||||
protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any
|
||||
public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: totally.normal.pkg.`Check>Enum>Entry`): kotlin.Int
|
||||
public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
// Static members
|
||||
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): totally.normal.pkg.`Check>Enum>Entry`
|
||||
public final /*synthesized*/ fun values(): kotlin.Array<totally.normal.pkg.`Check>Enum>Entry`>
|
||||
}
|
||||
|
||||
public object Delegate {
|
||||
private constructor Delegate()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public final operator fun getValue(/*0*/ thisRef: kotlin.Any?, /*1*/ property: kotlin.reflect.KProperty<*>): kotlin.Any?
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final class NormalClass {
|
||||
public constructor NormalClass()
|
||||
public final fun `check$member`(): kotlin.Unit
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public object NormalObject {
|
||||
private constructor NormalObject()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public final data class Pair {
|
||||
public constructor Pair(/*0*/ first: kotlin.Int, /*1*/ `next,one`: kotlin.Int)
|
||||
public final val first: kotlin.Int
|
||||
public final val `next,one`: kotlin.Int
|
||||
public final operator /*synthesized*/ fun component1(): kotlin.Int
|
||||
public final operator /*synthesized*/ fun component2(): kotlin.Int
|
||||
public final /*synthesized*/ fun copy(/*0*/ first: kotlin.Int = ..., /*1*/ `next,one`: kotlin.Int = ...): totally.normal.pkg.Pair
|
||||
public open override /*1*/ /*synthesized*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*synthesized*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*synthesized*/ fun toString(): kotlin.String
|
||||
}
|
||||
public typealias `check\typealias` = kotlin.Any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -30,6 +30,12 @@ public class DiagnosticsNativeTestGenerated extends AbstractDiagnosticsNativeTes
|
||||
runTest("compiler/testData/diagnostics/nativeTests/annotationConstructorCallNative.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("identifiers.kt")
|
||||
public void testIdentifiers() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/nativeTests/identifiers.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("sharedImmutable.kt")
|
||||
public void testSharedImmutable() throws Exception {
|
||||
|
||||
@@ -228,6 +228,7 @@ enum class LanguageFeature(
|
||||
ProhibitNonExhaustiveWhenOnAlgebraicTypes(KOTLIN_1_7, kind = BUG_FIX),
|
||||
UseBuilderInferenceWithoutAnnotation(KOTLIN_1_7),
|
||||
ProhibitSmartcastsOnPropertyFromAlienBaseClass(KOTLIN_1_7, kind = BUG_FIX),
|
||||
ProhibitInvalidCharsInNativeIdentifiers(KOTLIN_1_7, kind = BUG_FIX),
|
||||
|
||||
// Temporarily disabled, see KT-27084/KT-22379
|
||||
SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX),
|
||||
|
||||
+1
@@ -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)
|
||||
}
|
||||
|
||||
+81
@@ -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 = "\"")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user