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 c597518e7d0..8011c86065a 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 @@ -1073,6 +1073,9 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val INVALID_CHARACTERS by error(PositioningStrategy.NAME_IDENTIFIER) { parameter("message") } + val DANGEROUS_CHARACTERS by warning(PositioningStrategy.NAME_IDENTIFIER) { + parameter("characters") + } val EQUALITY_NOT_APPLICABLE by error { parameter("operator") 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 a3bb27e9db4..ca0dec65897 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 @@ -557,6 +557,7 @@ object FirErrors { val UNDERSCORE_IS_RESERVED by error0(SourceElementPositioningStrategies.NAME_IDENTIFIER) val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error0(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() val INCOMPATIBLE_ENUM_COMPARISON_ERROR by error2() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt index d73430795dc..28335828375 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt @@ -31,7 +31,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() { FirExposedVisibilityDeclarationChecker, FirCyclicTypeBoundsChecker, FirExpectActualDeclarationChecker, - FirInvalidCharactersChecker + FirInvalidAndDangerousCharactersChecker ) override val functionCheckers: Set diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInvalidCharactersChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInvalidAndDangerousCharactersChecker.kt similarity index 61% rename from compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInvalidCharactersChecker.kt rename to compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInvalidAndDangerousCharactersChecker.kt index 1a72cc1b491..c109d29322a 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInvalidCharactersChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInvalidAndDangerousCharactersChecker.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration -import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.fir.FirFakeSourceElementKind import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext @@ -15,9 +14,13 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.name.Name -object FirInvalidCharactersChecker : FirBasicDeclarationChecker() { +object FirInvalidAndDangerousCharactersChecker : FirBasicDeclarationChecker() { + // See The Java Virtual Machine Specification, section 4.7.9.1 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.9.1 private val INVALID_CHARS = setOf('.', ';', '[', ']', '/', '<', '>', ':', '\\') + // These characters can cause problems on Windows. '?*"|' are not allowed in file names, and % leads to unexpected env var expansion. + private val DANGEROUS_CHARS = setOf('?', '*', '"', '|', '%') + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { val source = declaration.source when (declaration) { @@ -32,19 +35,26 @@ object FirInvalidCharactersChecker : FirBasicDeclarationChecker() { } private fun checkNameAndReport(name: Name, source: FirSourceElement?, context: CheckerContext, reporter: DiagnosticReporter) { - val nameString = name.asString() if (source != null && source.kind !is FirFakeSourceElementKind && - source.elementType != KtNodeTypes.DESTRUCTURING_DECLARATION && - !name.isSpecial && - nameString.any { it in INVALID_CHARS } + !name.isSpecial ) { - reporter.reportOn( - source, - FirErrors.INVALID_CHARACTERS, - "contains illegal characters: ${INVALID_CHARS.intersect(nameString.toSet()).joinToString("")}", - context - ) + val nameString = name.asString() + if (nameString.any { it in INVALID_CHARS }) { + reporter.reportOn( + source, + FirErrors.INVALID_CHARACTERS, + "contains illegal characters: ${INVALID_CHARS.intersect(nameString.toSet()).joinToString("")}", + context + ) + } else if (nameString.any { it in DANGEROUS_CHARS }) { + reporter.reportOn( + source, + FirErrors.DANGEROUS_CHARACTERS, + DANGEROUS_CHARS.intersect(nameString.toSet()).joinToString(""), + context + ) + } } } } \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 851bd4bf5d5..bc4d2a93d92 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -108,6 +108,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CREATING_AN_INSTA import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CYCLIC_CONSTRUCTOR_DELEGATION_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CYCLIC_GENERIC_UPPER_BOUND import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CYCLIC_INHERITANCE_HIERARCHY +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DANGEROUS_CHARACTERS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_NOT_PROPERTY_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_OVERRIDE_CONFLICT import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DATA_CLASS_VARARG_PARAMETER @@ -1404,6 +1405,11 @@ class FirDefaultErrorMessages { "Name {0}", STRING ) + map.put( + DANGEROUS_CHARACTERS, + "Name contains characters which can cause problems on Windows: {0}", + STRING + ) map.put( EQUALITY_NOT_APPLICABLE, "Operator ''{0}'' cannot be applied to ''{1}'' and ''{2}''", diff --git a/compiler/testData/diagnostics/tests/declarationChecks/nameWithDangerousCharacters.fir.kt b/compiler/testData/diagnostics/tests/declarationChecks/nameWithDangerousCharacters.fir.kt deleted file mode 100644 index 28615d6a40a..00000000000 --- a/compiler/testData/diagnostics/tests/declarationChecks/nameWithDangerousCharacters.fir.kt +++ /dev/null @@ -1,12 +0,0 @@ -class `A?B` -class `A*B` -class `A"B` -class `A|B` -class `A%B` - -fun `?*"|%`(): Int { - val `?` = 0 - return `?` -} - -val `"a"+"b"` = "c" diff --git a/compiler/testData/diagnostics/tests/declarationChecks/nameWithDangerousCharacters.kt b/compiler/testData/diagnostics/tests/declarationChecks/nameWithDangerousCharacters.kt index 44860a1c6a1..7659cceaa6c 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/nameWithDangerousCharacters.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/nameWithDangerousCharacters.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL class `A?B` class `A*B` class `A"B` diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 1237570f495..e6717b001ce 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -2883,6 +2883,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.DANGEROUS_CHARACTERS) { firDiagnostic -> + DangerousCharactersImpl( + firDiagnostic.a, + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirErrors.EQUALITY_NOT_APPLICABLE) { firDiagnostic -> EqualityNotApplicableImpl( firDiagnostic.a, diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index e3e1c9ecd6c..3e8a0a55790 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -2016,6 +2016,11 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val message: String } + abstract class DangerousCharacters : KtFirDiagnostic() { + override val diagnosticClass get() = DangerousCharacters::class + abstract val characters: String + } + abstract class EqualityNotApplicable : KtFirDiagnostic() { override val diagnosticClass get() = EqualityNotApplicable::class abstract val operator: String diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index d2ca5b2844f..0b4ab368659 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -3247,6 +3247,14 @@ internal class InvalidCharactersImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class DangerousCharactersImpl( + override val characters: String, + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.DangerousCharacters(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class EqualityNotApplicableImpl( override val operator: String, override val leftType: KtType,