diff --git a/compiler/testData/diagnostics/nativeTests/identifiers.kt b/compiler/testData/diagnostics/nativeTests/identifiers.kt new file mode 100644 index 00000000000..941e92cbc7b --- /dev/null +++ b/compiler/testData/diagnostics/nativeTests/identifiers.kt @@ -0,0 +1,68 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE -MISSING_DEPENDENCY_SUPERCLASS + +// FIXME: rename identifiers.kt + +// FILE: 1.kt +package `check.pkg` + +// FILE: 2.kt +package totally.normal.pkg + +class `Check.Class` +class NormalClass { + fun `check$member`() {} +} + +object `Check;Object` +object NormalObject + +data class Pair(val first: Int, val `next,one`: Int) + +object Delegate { + operator fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): Any? = null +} + +fun `check(function`() { + val `check)variable` = 1 + val `check[delegated[variable` by Delegate + + val normalVariable = 2 + val normalDelegatedVariable by Delegate + + val (check, `destructuring]declaration`) = Pair(1, 2) +} + +fun normalFunction() {} + +val `check{property` = 1 +val `check}delegated}property` by Delegate +val normalProperty = 2 +val normalDelegatedProperty by Delegate + +fun checkValueParameter(`check/parameter`: Int) {} + +fun <`check, normalTypeParameter> checkTypeParameter() {} + +enum class `Check>Enum>Entry` { + `CHECK:ENUM:ENTRY`; +} + +typealias `check\typealias` = Any + +fun `check&`() {} + +fun `check~`() {} + +fun `check*`() {} + +fun `check?`() {} + +fun `check#`() {} + +fun `check|`() {} + +fun `check§`() {} + +fun `check%`() {} + +fun `check@`() {} diff --git a/compiler/testData/diagnostics/nativeTests/identifiers.txt b/compiler/testData/diagnostics/nativeTests/identifiers.txt new file mode 100644 index 00000000000..aea6d3121b2 --- /dev/null +++ b/compiler/testData/diagnostics/nativeTests/identifiers.txt @@ -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 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.EnumEnum>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.ArrayEnum>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 + } + } +} + diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsNativeTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsNativeTestGenerated.java index 053b55897fd..b52a44e7314 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsNativeTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticsNativeTestGenerated.java @@ -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 { diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 4323c680b0e..84822240a30 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -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), diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt index a5c4ddb7d9d..96b1f350ca2 100644 --- a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/DefaultErrorMessagesNative.kt @@ -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); } } diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt index b97951bf852..fcebb3b10ed 100644 --- a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/ErrorsNative.kt @@ -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(Severity.ERROR) @JvmField val VARIABLE_IN_ENUM = DiagnosticFactory0.create(Severity.WARNING) + @JvmField + val INVALID_CHARACTERS_NATIVE = DiagnosticFactoryForDeprecation1.create(LanguageFeature.ProhibitInvalidCharsInNativeIdentifiers) + init { Errors.Initializer.initializeFactoryNames(ErrorsNative::class.java) } diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeIdentifierChecker.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeIdentifierChecker.kt new file mode 100644 index 00000000000..3ac713f6b14 --- /dev/null +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/diagnostics/NativeIdentifierChecker.kt @@ -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 = "\"") + ) + ) + } + } + } +} diff --git a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt index 9bc8aab4834..1ae04268eed 100644 --- a/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt +++ b/native/frontend/src/org/jetbrains/kotlin/resolve/konan/platform/NativePlatformConfigurator.kt @@ -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() } override fun configureModuleDependentCheckers(container: StorageComponentContainer) {