diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 0d11ac3d9ee..ee62fc9ecd4 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt @@ -3036,7 +3036,7 @@ sealed interface KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = ResolvedToUnderscoreNamedCatchParameter::class } - interface InvalidCharacters : KtFirDiagnostic { + interface InvalidCharacters : KtFirDiagnostic { override val diagnosticClass get() = InvalidCharacters::class val message: String } diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index d02393c7048..b978d01b71b 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -3661,7 +3661,7 @@ internal class InvalidCharactersImpl( override val message: String, firDiagnostic: KtPsiDiagnostic, token: KtLifetimeToken, -) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.InvalidCharacters +) : KtAbstractFirDiagnostic(firDiagnostic, token), KtFirDiagnostic.InvalidCharacters internal class DangerousCharactersImpl( override val characters: String, diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 7dc6fcd7663..1d840aaccb7 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -1515,7 +1515,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val UNDERSCORE_IS_RESERVED by error(PositioningStrategy.NAME_IDENTIFIER) val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error(PositioningStrategy.NAME_IDENTIFIER) val RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER by warning() - val INVALID_CHARACTERS by error(PositioningStrategy.NAME_IDENTIFIER) { + val INVALID_CHARACTERS by error(PositioningStrategy.NAME_IDENTIFIER) { parameter("message") } val DANGEROUS_CHARACTERS by warning(PositioningStrategy.NAME_IDENTIFIER) { diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt index f54f3e51892..5838839bf23 100644 --- a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/JsDeclarationCheckers.kt @@ -50,6 +50,7 @@ object JsDeclarationCheckers : DeclarationCheckers() { override val fileCheckers: Set get() = setOf( + FirJsPackageDirectiveChecker, FirJsNameClashFileTopLevelDeclarationsChecker ) } diff --git a/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsPackageDirectiveChecker.kt b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsPackageDirectiveChecker.kt new file mode 100644 index 00000000000..ffeb5edaac8 --- /dev/null +++ b/compiler/fir/checkers/checkers.js/src/org/jetbrains/kotlin/fir/analysis/js/checkers/declaration/FirJsPackageDirectiveChecker.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2023 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.fir.analysis.js.checkers.declaration + +import org.jetbrains.kotlin.KtNodeTypes +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFileChecker +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.analysis.forEachChildOfType +import org.jetbrains.kotlin.fir.analysis.js.checkers.sanitizeName +import org.jetbrains.kotlin.fir.declarations.FirFile +import org.jetbrains.kotlin.fir.packageFqName +import org.jetbrains.kotlin.text + +object FirJsPackageDirectiveChecker: FirFileChecker() { + // inspired by FirJsNameCharsChecker.check() + override fun check(declaration: FirFile, context: CheckerContext, reporter: DiagnosticReporter) { + if (declaration.packageFqName.isRoot) return + if (context.languageVersionSettings.supportsFeature(LanguageFeature.JsAllowInvalidCharsIdentifiersEscaping)) return + + declaration.packageDirective.source?.forEachChildOfType(setOf(KtNodeTypes.REFERENCE_EXPRESSION)) { + val name = it.text.toString() + if (sanitizeName(name) != name) { + reporter.reportOn( + it, + FirErrors.INVALID_CHARACTERS, + "$name contains illegal characters", + context, + ) + } + } + } +} diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 00f94e5a72e..d76ddfb8dd0 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -765,7 +765,7 @@ object FirErrors { val UNDERSCORE_IS_RESERVED by error0(SourceElementPositioningStrategies.NAME_IDENTIFIER) val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error0(SourceElementPositioningStrategies.NAME_IDENTIFIER) val RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER by warning0() - val INVALID_CHARACTERS by error1(SourceElementPositioningStrategies.NAME_IDENTIFIER) + val INVALID_CHARACTERS by error1(SourceElementPositioningStrategies.NAME_IDENTIFIER) val DANGEROUS_CHARACTERS by warning1(SourceElementPositioningStrategies.NAME_IDENTIFIER) val EQUALITY_NOT_APPLICABLE by error3() val EQUALITY_NOT_APPLICABLE_WARNING by warning3() diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/illegalPackageName.fir.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/illegalPackageName.fir.kt deleted file mode 100644 index 4d78f0325d5..00000000000 --- a/compiler/testData/diagnostics/testsWithJsStdLib/name/illegalPackageName.fir.kt +++ /dev/null @@ -1,3 +0,0 @@ -package `//` - -class A diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/illegalPackageName.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/illegalPackageName.kt index 6e85ce4f119..062b71f6bca 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/name/illegalPackageName.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/illegalPackageName.kt @@ -1,3 +1,24 @@ -package `//` +// FIR_IDENTICAL +// FILE: slashes.kt +package a.`//`.b.`/`.c +class Slashes -class A \ No newline at end of file +// FILE: space.kt +package ` ` +class Space + +// FILE: less.kt +package `<` +class Less + +// FILE: more.kt +package `>` +class More + +// FILE: dash.kt +package `-` +class Dash + +// FILE: question.kt +package `?` +class Question diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/illegalPackageName.txt b/compiler/testData/diagnostics/testsWithJsStdLib/name/illegalPackageName.txt index ea87874d14d..56724a1134c 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLib/name/illegalPackageName.txt +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/illegalPackageName.txt @@ -1,11 +1,64 @@ package -package `//` { +package ` ` { - public final class A { - public constructor A() + public final class Space { + public constructor Space() 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 } } + +package `-` { + + public final class Dash { + public constructor Dash() + 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 + } +} + +package `>` { + + public final class More { + public constructor More() + 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 + } +} + +package `?` { + + public final class Question { + public constructor Question() + 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 + } +} + +package a { + + package a.`//` { + + package a.`//`.b { + + package a.`//`.b.`/` { + + package a.`//`.b.`/`.c { + + public final class Slashes { + public constructor Slashes() + 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 + } + } + } + } + } +} + diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/name/legalPackageName.kt b/compiler/testData/diagnostics/testsWithJsStdLib/name/legalPackageName.kt new file mode 100644 index 00000000000..336bec101f4 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/name/legalPackageName.kt @@ -0,0 +1,30 @@ +// FIR_IDENTICAL +// !LANGUAGE: +JsAllowInvalidCharsIdentifiersEscaping + +// FILE: slashes.kt +package a.`//`.b.`/`.c +class Slashes + +// FILE: slash.kt +package `/` +class Slash + +// FILE: space.kt +package ` ` +class Space + +// FILE: less.kt +package `<` +class Less + +// FILE: more.kt +package `>` +class More + +// FILE: dash.kt +package `-` +class Dash + +// FILE: question.kt +package `?` +class Question diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirPsiJsOldFrontendDiagnosticsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirPsiJsOldFrontendDiagnosticsTestGenerated.java index f1a8aa85d21..9f6f98092e6 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirPsiJsOldFrontendDiagnosticsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirPsiJsOldFrontendDiagnosticsTestGenerated.java @@ -790,6 +790,12 @@ public class FirPsiJsOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiJ runTest("compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameWithoutParameter.kt"); } + @Test + @TestMetadata("legalPackageName.kt") + public void testLegalPackageName() throws Exception { + runTest("compiler/testData/diagnostics/testsWithJsStdLib/name/legalPackageName.kt"); + } + @Test @TestMetadata("methodAndMethod.kt") public void testMethodAndMethod() throws Exception { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/DiagnosticsWithJsStdLibTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/DiagnosticsWithJsStdLibTestGenerated.java index 41f2597bd4c..757b51a297f 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/DiagnosticsWithJsStdLibTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/DiagnosticsWithJsStdLibTestGenerated.java @@ -790,6 +790,12 @@ public class DiagnosticsWithJsStdLibTestGenerated extends AbstractDiagnosticsTes runTest("compiler/testData/diagnostics/testsWithJsStdLib/name/jsNameWithoutParameter.kt"); } + @Test + @TestMetadata("legalPackageName.kt") + public void testLegalPackageName() throws Exception { + runTest("compiler/testData/diagnostics/testsWithJsStdLib/name/legalPackageName.kt"); + } + @Test @TestMetadata("methodAndMethod.kt") public void testMethodAndMethod() throws Exception {