From 83895c49c526ac226404031b777417673d80977c Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Thu, 22 Jul 2021 23:47:01 +0300 Subject: [PATCH] [FIR] Implement INVALID_CHARACTERS --- .../diagnostics/FirDiagnosticsList.kt | 7 +- .../diagnostics/model/DiagnosticData.kt | 2 +- .../fir/analysis/diagnostics/FirErrors.kt | 5 +- .../checkers/CommonDeclarationCheckers.kt | 3 +- .../FirInvalidCharactersChecker.kt | 50 ++++++++++ .../diagnostics/FirDefaultErrorMessages.kt | 6 ++ .../LightTreePositioningStrategies.kt | 2 +- .../SourceElementPositioningStrategies.kt | 6 +- .../diagnostics/PositioningStrategies.kt | 19 ++-- .../diagnostics/tests/BacktickNames.fir.kt | 94 ------------------- .../diagnostics/tests/BacktickNames.kt | 1 + .../diagnostics/KtFirDataClassConverters.kt | 7 ++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 5 + .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 8 ++ 14 files changed, 99 insertions(+), 116 deletions(-) create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInvalidCharactersChecker.kt delete mode 100644 compiler/testData/diagnostics/tests/BacktickNames.fir.kt 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 e734946ca98..c597518e7d0 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 @@ -1068,8 +1068,11 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter("actualType") } - val UNDERSCORE_IS_RESERVED by error(PositioningStrategy.RESERVED_UNDERSCORE) - val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error(PositioningStrategy.RESERVED_UNDERSCORE) + val UNDERSCORE_IS_RESERVED by error(PositioningStrategy.NAME_IDENTIFIER) + val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error(PositioningStrategy.NAME_IDENTIFIER) + val INVALID_CHARACTERS by error(PositioningStrategy.NAME_IDENTIFIER) { + parameter("message") + } val EQUALITY_NOT_APPLICABLE by error { parameter("operator") diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticData.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticData.kt index 75839603bc5..04669d7403d 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticData.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/model/DiagnosticData.kt @@ -69,7 +69,7 @@ enum class PositioningStrategy(private val strategy: String? = null) { FUN_MODIFIER, SUSPEND_MODIFIER, FUN_INTERFACE, - RESERVED_UNDERSCORE, + NAME_IDENTIFIER, QUESTION_MARK_BY_TYPE, ANNOTATION_USE_SITE, ASSIGNMENT_LHS, 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 66c589d114f..a3bb27e9db4 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 @@ -554,8 +554,9 @@ object FirErrors { val DELEGATE_SPECIAL_FUNCTION_AMBIGUITY by error2>>() val DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE by error2>>() val DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH by error3() - val UNDERSCORE_IS_RESERVED by error0(SourceElementPositioningStrategies.RESERVED_UNDERSCORE) - val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error0(SourceElementPositioningStrategies.RESERVED_UNDERSCORE) + 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 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 8047c4f520b..d73430795dc 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 @@ -30,7 +30,8 @@ object CommonDeclarationCheckers : DeclarationCheckers() { FirInfixFunctionDeclarationChecker, FirExposedVisibilityDeclarationChecker, FirCyclicTypeBoundsChecker, - FirExpectActualDeclarationChecker + FirExpectActualDeclarationChecker, + FirInvalidCharactersChecker ) 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/FirInvalidCharactersChecker.kt new file mode 100644 index 00000000000..1a72cc1b491 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirInvalidCharactersChecker.kt @@ -0,0 +1,50 @@ +/* + * 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.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 +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.name.Name + +object FirInvalidCharactersChecker : FirBasicDeclarationChecker() { + private val INVALID_CHARS = setOf('.', ';', '[', ']', '/', '<', '>', ':', '\\') + + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + val source = declaration.source + when (declaration) { + is FirRegularClass -> checkNameAndReport(declaration.name, source, context, reporter) + is FirSimpleFunction -> checkNameAndReport(declaration.name, source, context, reporter) + is FirTypeParameter -> checkNameAndReport(declaration.name, source, context, reporter) + is FirProperty -> checkNameAndReport(declaration.name, source, context, reporter) + is FirTypeAlias -> checkNameAndReport(declaration.name, source, context, reporter) + is FirValueParameter -> checkNameAndReport(declaration.name, source, context, reporter) + else -> return + } + } + + 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 } + ) { + reporter.reportOn( + source, + FirErrors.INVALID_CHARACTERS, + "contains illegal characters: ${INVALID_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 bb23ff29008..851bd4bf5d5 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 @@ -228,6 +228,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INNER_CLASS_INSID import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INNER_CLASS_OF_GENERIC_THROWABLE_SUBCLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INSTANCE_ACCESS_BEFORE_SUPER_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INTERFACE_WITH_SUPERCLASS +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_CHARACTERS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_IF_AS_EXPRESSION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_TYPE_OF_ANNOTATION_MEMBER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVISIBLE_REFERENCE @@ -1398,6 +1399,11 @@ class FirDefaultErrorMessages { UNDERSCORE_USAGE_WITHOUT_BACKTICKS, "Names _, __, ___, ... can be used only in back-ticks (`_`, `__`, `___`, ...)" ) + map.put( + INVALID_CHARACTERS, + "Name {0}", + STRING + ) map.put( EQUALITY_NOT_APPLICABLE, "Operator ''{0}'' cannot be applied to ''{1}'' and ''{2}''", diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt index 678909e604f..3efdda344d2 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/LightTreePositioningStrategies.kt @@ -708,7 +708,7 @@ object LightTreePositioningStrategies { } } - val RESERVED_UNDERSCORE: LightTreePositioningStrategy = object : LightTreePositioningStrategy() { + val NAME_IDENTIFIER: LightTreePositioningStrategy = object : LightTreePositioningStrategy() { override fun mark( node: LighterASTNode, startOffset: Int, diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt index 80de9f41df6..e619e2b21eb 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/SourceElementPositioningStrategies.kt @@ -238,9 +238,9 @@ object SourceElementPositioningStrategies { PositioningStrategies.TYPE_PARAMETERS_LIST ) - val RESERVED_UNDERSCORE = SourceElementPositioningStrategy( - LightTreePositioningStrategies.RESERVED_UNDERSCORE, - PositioningStrategies.RESERVED_UNDERSCORE + val NAME_IDENTIFIER = SourceElementPositioningStrategy( + LightTreePositioningStrategies.NAME_IDENTIFIER, + PositioningStrategies.NAME_IDENTIFIER ) val QUESTION_MARK_BY_TYPE = SourceElementPositioningStrategy( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index e8f54e1ebe6..5ce1bf90621 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -837,18 +837,13 @@ object PositioningStrategies { } } - val RESERVED_UNDERSCORE: PositioningStrategy = object : PositioningStrategy() { - override fun mark(element: PsiElement): List { - if (element is PsiNameIdentifierOwner) { - val nameIdentifier = element.nameIdentifier - if (nameIdentifier != null) { - return super.mark(nameIdentifier) - } - } else if (element is KtLabelReferenceExpression) { - return super.mark(element.getReferencedNameElement()) - } - - return super.mark(element) + val NAME_IDENTIFIER: PositioningStrategy = object : PositioningStrategy() { + private fun KtTypeElement.getReferencedTypeExpression(): KtElement? { + return when (this) { + is KtUserType -> referenceExpression + is KtNullableType -> innerType?.getReferencedTypeExpression() + is KtDefinitelyNotNullType -> innerType?.getReferencedTypeExpression() + else -> null } } diff --git a/compiler/testData/diagnostics/tests/BacktickNames.fir.kt b/compiler/testData/diagnostics/tests/BacktickNames.fir.kt deleted file mode 100644 index a1f1b49f01c..00000000000 --- a/compiler/testData/diagnostics/tests/BacktickNames.fir.kt +++ /dev/null @@ -1,94 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER -// TODO Uncomment all the examples when there will be no problems with light classes -//package `foo.bar` - -// TODO: Uncomment after fixing KT-9416 -//import kotlin.Deprecated as `deprecate\entity` - -//@`deprecate\entity`("") data class Pair(val x: Int, val y: Int) - -// Names should not contains characters: '.', ';', '[', ']', '/', '<', '>', ':', '\\' -//class `class.name` -class `class;name` -class `class[name` -class `class]name` -//class `class/name` -class `classname` -class `class:name` -class `class\name` - -class ` ` {} -class ` ` - -//val `val.X` = 10 -val `val;X` = 10 -val `val[X` = 10 -val `val]X` = 10 -//val `val/X` = 10 -val `valX` = 10 -val `val:X` = 10 -val `val\X` = 10 - -val `;` = 1 -val `[` = 2 -val `]` = 3 -val `<` = 4 - -val `>` = 5 -val `:` = 6 -val `\` = 7 -val `<>` = 8 - -val `[]` = 9 -val `[;]` = 10 - -// TODO Uncomment when there will be no problems with light classes (Error: Invalid formal type parameter (must be a valid Java identifier)) -//class AWithTypeParameter<`T:K`> {} -//fun <`T/K`> genericFun(x: `T/K`) {} - -class B(val `a:b`: Int, val `c:d`: Int) - -val ff: (`x:X`: Int) -> Unit = {} -val fg: ((`x:X`: Int) -> Unit) -> Unit = {} -val fh: ((Int) -> ((`x:X`: Int) -> Unit) -> Unit) = {{}} - -fun f(x: Int, g: (Int) -> Unit) = g(x) - -data class Data(val x: Int, val y: Int) - -class A() { - init { - val `a:b` = 10 - } - - fun g(`x:y`: Int) { - val `s:` = 30 - } -} - -fun `foo:bar`(`\arg`: Int): Int { - val (`a:b`, c) = Data(10, 20) - val `a\b` = 10 - - fun localFun() {} - - for (`x/y` in 0..10) { - } - - f(10) { - `x:z`: Int -> localFun() - } - - f(20, fun(`x:z`: Int): Unit {}) - - try { - val `a:` = 10 - } - catch (`e:a`: Exception) { - val `b:` = 20 - } - - return `\arg` -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/BacktickNames.kt b/compiler/testData/diagnostics/tests/BacktickNames.kt index 020a9d9a86d..064ed03ceab 100644 --- a/compiler/testData/diagnostics/tests/BacktickNames.kt +++ b/compiler/testData/diagnostics/tests/BacktickNames.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER -UNUSED_ANONYMOUS_PARAMETER // TODO Uncomment all the examples when there will be no problems with light classes //package `foo.bar` 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 a2bf45a82e0..1237570f495 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 @@ -2876,6 +2876,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.INVALID_CHARACTERS) { firDiagnostic -> + InvalidCharactersImpl( + 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 8ba2a8a9805..e3e1c9ecd6c 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 @@ -2011,6 +2011,11 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = UnderscoreUsageWithoutBackticks::class } + abstract class InvalidCharacters : KtFirDiagnostic() { + override val diagnosticClass get() = InvalidCharacters::class + abstract val message: 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 bd2db5934ad..d2ca5b2844f 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 @@ -3239,6 +3239,14 @@ internal class UnderscoreUsageWithoutBackticksImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class InvalidCharactersImpl( + override val message: String, + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.InvalidCharacters(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class EqualityNotApplicableImpl( override val operator: String, override val leftType: KtType,