From ea2d9f7c0cbaac3d6d96134f14d2d771a8cc4c18 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Thu, 22 Apr 2021 14:01:02 +0300 Subject: [PATCH] [FIR] Implement UNDERSCORE_IS_RESERVED, UNDERSCORE_USAGE_WITHOUT_BACKTICKS diagnostics (psi only) --- .../generator/diagnostics/DiagnosticData.kt | 1 + .../diagnostics/FirDiagnosticsList.kt | 2 + .../fir/analysis/diagnostics/FirErrors.kt | 2 + .../FirReservedUnderscoreCheckers.kt | 143 ++++++++++++++++++ .../LightTreePositioningStrategies.kt | 3 + .../SourceElementPositioningStrategies.kt | 5 + .../fir/checkers/CommonDeclarationCheckers.kt | 2 + .../fir/checkers/CommonExpressionCheckers.kt | 3 +- .../diagnostics/PositioningStrategies.kt | 49 ++++-- .../kotlin/diagnostics/PositioningStrategy.kt | 4 + .../diagnostics/tests/Underscore.fir.kt | 44 +++--- .../testData/diagnostics/tests/Underscore.kt | 4 + .../tests/UnderscoreUsageInCall.fir.kt | 42 ----- .../tests/UnderscoreUsageInCall.kt | 1 + ...UnderscoreUsageInCallableRefTypeLHS.fir.kt | 11 -- .../UnderscoreUsageInCallableRefTypeLHS.kt | 1 + .../tests/UnderscoreUsageInType.fir.kt | 12 -- .../tests/UnderscoreUsageInType.kt | 1 + ...rscoreUsageInVariableAsFunctionCall.fir.kt | 6 - ...UnderscoreUsageInVariableAsFunctionCall.kt | 1 + .../underscore.fir.kt | 18 +-- .../resolve/underscoreInCatchBlock.fir.kt | 2 +- ...scoreInCatchBlockWithEnabledFeature.fir.kt | 2 +- .../fir/handlers/FirDiagnosticsHandler.kt | 7 +- .../decimal-integer-literals/p-1/neg/2.1.kt | 4 +- .../decimal-integer-literals/p-1/neg/2.2.kt | 4 +- .../decimal-integer-literals/p-1/neg/2.3.kt | 4 +- .../diagnostics/KtFirDataClassConverters.kt | 12 ++ .../api/fir/diagnostics/KtFirDiagnostics.kt | 8 + .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 14 ++ 30 files changed, 291 insertions(+), 121 deletions(-) create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReservedUnderscoreCheckers.kt delete mode 100644 compiler/testData/diagnostics/tests/UnderscoreUsageInCall.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/UnderscoreUsageInCallableRefTypeLHS.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/UnderscoreUsageInType.fir.kt delete mode 100644 compiler/testData/diagnostics/tests/UnderscoreUsageInVariableAsFunctionCall.fir.kt diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/DiagnosticData.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/DiagnosticData.kt index 4aa0f309100..b763933bd81 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/DiagnosticData.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/DiagnosticData.kt @@ -67,6 +67,7 @@ enum class PositioningStrategy(private val strategy: String? = null) { FUN_MODIFIER, SUSPEND_MODIFIER, FUN_INTERFACE, + RESERVED_UNDERSCORE, ; 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 8e438f46e8d..a3f994cc4ca 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 @@ -696,6 +696,8 @@ object DIAGNOSTICS_LIST : DiagnosticList() { parameter("expectedFunctionSignature") parameter>>("candidates") } + val UNDERSCORE_IS_RESERVED by error(PositioningStrategy.RESERVED_UNDERSCORE) + val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error(PositioningStrategy.RESERVED_UNDERSCORE) } val TYPE_ALIAS by object : DiagnosticGroup("Type alias") { 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 8c0eef37386..072455dce69 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 @@ -409,6 +409,8 @@ object FirErrors { val DELEGATE_SPECIAL_FUNCTION_MISSING by error3() val DELEGATE_SPECIAL_FUNCTION_AMBIGUITY by error2>>() val DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE by error2>>() + val UNDERSCORE_IS_RESERVED by error0(SourceElementPositioningStrategies.RESERVED_UNDERSCORE) + val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error0(SourceElementPositioningStrategies.RESERVED_UNDERSCORE) // Type alias val TOPLEVEL_TYPEALIASES_ONLY by error0() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReservedUnderscoreCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReservedUnderscoreCheckers.kt new file mode 100644 index 00000000000..65f594aa67b --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReservedUnderscoreCheckers.kt @@ -0,0 +1,143 @@ +/* + * 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.expression + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiNameIdentifierOwner +import com.intellij.psi.impl.source.tree.LeafPsiElement +import org.jetbrains.kotlin.fir.FirSourceElement +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker +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.fir.expressions.* +import org.jetbrains.kotlin.fir.psi +import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType + +object FirReservedUnderscoreExpressionChecker : FirBasicExpressionChecker() { + override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { + if (expression is FirFunctionCall) { + reportIfUnderscore( + expression.calleeReference.psi?.text, expression.source, context, reporter, + isExpression = true + ) + + for (argument in expression.arguments) { + if (argument is FirNamedArgumentExpression) { + reportIfUnderscore(argument.psi?.firstChild?.text, argument.source, context, reporter) + } + } + } else if (expression is FirQualifiedAccess) { + fun processQualifiedAccess(psi: PsiElement?) { + if (psi is KtNameReferenceExpression) { + reportIfUnderscore(psi.text, expression.source, context, reporter, isExpression = true) + } else if (psi is KtDotQualifiedExpression || psi is KtCallableReferenceExpression) { + processQualifiedAccess(psi.firstChild) + processQualifiedAccess(psi.lastChild) + } + } + + val psi = expression.psi + if (psi != null && psi.parent !is KtDotQualifiedExpression && psi.parent !is KtCallableReferenceExpression) { + processQualifiedAccess(psi) + } + } else if (expression is FirGetClassCall) { + for (argument in expression.argumentList.arguments) { + reportIfUnderscore(argument.psi?.text, expression.source, context, reporter, isExpression = true) + } + } else if (expression is FirReturnExpression) { + reportIfUnderscore(expression.target.labelName, expression.source, context, reporter) + } + } +} + +object FirReservedUnderscoreDeclarationChecker : FirBasicDeclarationChecker() { + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + if ( + declaration is FirClass<*> || + declaration is FirFunction<*> || + declaration is FirTypeParameter || + declaration is FirProperty || + declaration is FirTypeAlias + ) { + + reportIfUnderscore(declaration, context, reporter) + + if (declaration is FirFunction<*>) { + for (parameter in declaration.valueParameters) { + reportIfUnderscore( + parameter, + context, + reporter, + isSingleUnderscoreAllowed = declaration is FirAnonymousFunction || declaration is FirPropertyAccessor + ) + } + } + } else if (declaration is FirFile) { + for (import in declaration.imports) { + reportIfUnderscore(import.aliasName?.asString(), import.source, context, reporter) + } + } + } +} + +private fun reportIfUnderscore( + declaration: FirDeclaration, + context: CheckerContext, + reporter: DiagnosticReporter, + isSingleUnderscoreAllowed: Boolean = false +) { + val rawIdentifier = (declaration.psi as? PsiNameIdentifierOwner)?.nameIdentifier?.text ?: return + reportIfUnderscore(rawIdentifier, declaration.source, context, reporter, isSingleUnderscoreAllowed) + + fun reportIfAnyDescendantIfUnderscore(typeRef: FirTypeRef?) { + if (typeRef == null) return + + if (typeRef.psi?.anyDescendantOfType { isUnderscore(it.text) } == true) { + reporter.reportOn( + typeRef.source, + FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS, + context + ) + } + } + + if (declaration is FirValueParameter) { + val psi = declaration.returnTypeRef.psi + if (psi !is KtFunctionLiteral && psi !is KtParameter) { + reportIfAnyDescendantIfUnderscore(declaration.returnTypeRef) + } + } else if (declaration is FirFunction<*>) { + reportIfAnyDescendantIfUnderscore(declaration.receiverTypeRef) + } +} + +private fun reportIfUnderscore( + text: String?, + source: FirSourceElement?, + context: CheckerContext, + reporter: DiagnosticReporter, + isSingleUnderscoreAllowed: Boolean = false, + isExpression: Boolean = false +) { + if (text == null || isSingleUnderscoreAllowed && text == "_") { + return + } + + if (isUnderscore(text)) { + reporter.reportOn( + source, + if (isExpression) FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS else FirErrors.UNDERSCORE_IS_RESERVED, + context + ) + } +} + +private fun isUnderscore(text: String) = text.all { it == '_' } \ No newline at end of file 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 e52b4eb3b72..2654b47292e 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 @@ -642,6 +642,9 @@ object LightTreePositioningStrategies { return markElement(tree.typeParametersList(node) ?: node, startOffset, endOffset, tree, node) } } + + val RESERVED_UNDERSCORE: LightTreePositioningStrategy = object : LightTreePositioningStrategy() { + } } fun FirSourceElement.hasValOrVar(): Boolean = 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 8da4001479d..9e9f562d220 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 @@ -228,4 +228,9 @@ object SourceElementPositioningStrategies { LightTreePositioningStrategies.TYPE_PARAMETERS_LIST, PositioningStrategies.TYPE_PARAMETERS_LIST ) + + val RESERVED_UNDERSCORE = SourceElementPositioningStrategy( + LightTreePositioningStrategies.RESERVED_UNDERSCORE, + PositioningStrategies.RESERVED_UNDERSCORE + ) } diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt index c546ed99cfe..88671c14831 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonDeclarationCheckers.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.analysis.cfa.FirPropertyInitializationAnalyzer import org.jetbrains.kotlin.fir.analysis.cfa.FirReturnsImpliesAnalyzer import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker import org.jetbrains.kotlin.fir.analysis.checkers.declaration.* +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirReservedUnderscoreDeclarationChecker object CommonDeclarationCheckers : DeclarationCheckers() { override val basicDeclarationCheckers: Set @@ -20,6 +21,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() { FirConflictsChecker, FirConflictingProjectionChecker, FirTypeConstraintsChecker, + FirReservedUnderscoreDeclarationChecker ) override val memberDeclarationCheckers: Set diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt index e387ec98150..5870332d7d3 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt @@ -15,6 +15,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { override val basicExpressionCheckers: Set get() = setOf( + FirReservedUnderscoreExpressionChecker ) override val qualifiedAccessCheckers: Set @@ -31,7 +32,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { FirTypeParameterInQualifiedAccessChecker, FirSealedClassConstructorCallChecker, FirUninitializedEnumChecker, - FirFunInterfaceConstructorReferenceChecker, + FirFunInterfaceConstructorReferenceChecker ) override val functionCallCheckers: Set diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index b0c17347c72..3eaf31eedd4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -10,6 +10,7 @@ import com.intellij.psi.PsiComment import com.intellij.psi.PsiElement import com.intellij.psi.PsiNameIdentifierOwner import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.impl.source.tree.LeafPsiElement import com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.cfg.UnreachableCode @@ -144,10 +145,10 @@ object PositioningStrategies { ClassKind -> { val startElement = element.modifierList?.getModifier(KtTokens.ENUM_KEYWORD) - ?: element.modifierList?.getModifier(KtTokens.ANNOTATION_KEYWORD) + ?: element.modifierList?.getModifier(KtTokens.ANNOTATION_KEYWORD) val endElement = element.node.findChildByType(classKindTokens)?.psi - ?: element.nameIdentifier + ?: element.nameIdentifier if (startElement != null && endElement != null) { return markRange(startElement, endElement) } else { @@ -160,7 +161,7 @@ object PositioningStrategies { } CallableKind -> { (callableDeclaration as? KtNamedFunction)?.funKeyword - ?: (callableDeclaration as? KtProperty)?.valOrVarKeyword + ?: (callableDeclaration as? KtProperty)?.valOrVarKeyword } ParameterShape -> { callableDeclaration?.let { it.receiverTypeReference ?: it.valueParameterList } @@ -239,7 +240,7 @@ object PositioningStrategies { val startElement = element.getModifierList()?.getModifier(KtTokens.ENUM_KEYWORD) ?: element.node.findChildByType(TokenSet.create(KtTokens.CLASS_KEYWORD, KtTokens.OBJECT_KEYWORD))?.psi - ?: element + ?: element return markRange(startElement, nameIdentifier) } @@ -264,13 +265,13 @@ object PositioningStrategies { is KtFunction -> { val endOfSignatureElement = element.typeReference - ?: element.valueParameterList - ?: element.nameIdentifier - ?: element + ?: element.valueParameterList + ?: element.nameIdentifier + ?: element val startElement = if (element is KtFunctionLiteral) { element.getReceiverTypeReference() - ?: element.getValueParameterList() - ?: element + ?: element.getValueParameterList() + ?: element } else element return markRange(startElement, endOfSignatureElement) } @@ -281,8 +282,8 @@ object PositioningStrategies { is KtPropertyAccessor -> { val endOfSignatureElement = element.returnTypeReference - ?: element.rightParenthesis - ?: element.namePlaceholder + ?: element.rightParenthesis + ?: element.namePlaceholder return markRange(element, endOfSignatureElement) } @@ -390,6 +391,7 @@ object PositioningStrategies { return markElement(nameIdentifier ?: element) } } + @JvmField val FOR_UNRESOLVED_REFERENCE: PositioningStrategy = object : PositioningStrategy() { override fun mark(element: KtReferenceExpression): List { @@ -783,6 +785,31 @@ 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 KtImportDirective) { + return super.mark(element.alias?.nameIdentifier ?: element) + } else if (element is KtReturnExpression) { + val prevSibling = element.getPrevSiblingIgnoringWhitespace() + if (prevSibling is KtContainerNode) { + return mark(prevSibling) + } + } else if (element !is LeafPsiElement) { + val ranges = element.collectDescendantsOfType { descendant -> descendant.text.all { it == '_' } } + .map { markSingleElement(it) } + if (ranges.isNotEmpty()) { + return ranges + } + } + return super.mark(element) + } + } + @JvmField val FUN_INTERFACE: PositioningStrategy = object : PositioningStrategy() { override fun mark(element: KtDeclaration): List { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategy.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategy.kt index 127d1136036..879d7ebeec9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategy.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategy.kt @@ -43,6 +43,10 @@ fun markElement(element: PsiElement): List { return listOf(TextRange(getStartOffset(element), getEndOffset(element))) } +fun markSingleElement(element: PsiElement): TextRange { + return TextRange(getStartOffset(element), getEndOffset(element)) +} + fun markNode(node: ASTNode): List { return markElement(node.psi) } diff --git a/compiler/testData/diagnostics/tests/Underscore.fir.kt b/compiler/testData/diagnostics/tests/Underscore.fir.kt index 1ab54eb4c44..af1ddccab0b 100644 --- a/compiler/testData/diagnostics/tests/Underscore.fir.kt +++ b/compiler/testData/diagnostics/tests/Underscore.fir.kt @@ -1,46 +1,50 @@ // !DIAGNOSTICS: -DEPRECATION -TOPLEVEL_TYPEALIASES_ONLY -import kotlin.Deprecated as ___ +import kotlin.Deprecated as ___ @___("") data class Pair(val x: Int, val y: Int) -class _<________> -val ______ = _() +class _<________> +val ______ = _() -fun __(___: Int, y: _?): Int { - val (_, z) = Pair(___ - 1, 42) - val (x, __________) = Pair(___ - 1, 42) - val ____ = x +fun __(___: Int, y: _?): Int { + val (_, z) = Pair(___ - 1, 42) + val (x, __________) = Pair(___ - 1, 42) + val ____ = x // in backquotes: allowed - val `_` = __________ + val `_` = __________ - val q = fun(_: Int, __: Int) {} + val q = fun(_: Int, __: Int) {} q(1, 2) - val _ = 56 + val _ = 56 - fun localFun(_: String) = 1 + fun localFun(_: String) = 1 - __@ return if (y != null) __(____, y) else __(`_`, ______) + __@ return if (y != null) __(____, y) else __(`_`, ______) } -class A1(val _: String) -class A2(_: String) { +class A1(val _: String) +class A2(_: String) { class B { - typealias _ = CharSequence + typealias _ = CharSequence } - val _: Int = 1 + val _: Int = 1 - fun _() {} + fun _() {} - fun foo(_: Double) {} + fun foo(_: Double) {} } // one underscore parameters for named function are still prohibited -fun oneUnderscore(_: Int) {} +fun oneUnderscore(_: Int) {} fun doIt(f: (Any?) -> Any?) = f(null) -val something = doIt { __ -> __ } +val something = doIt { __ -> __ } val something2 = doIt { _ -> 1 } + +var p: Int? + get() = null + set(_) {} diff --git a/compiler/testData/diagnostics/tests/Underscore.kt b/compiler/testData/diagnostics/tests/Underscore.kt index e9fbee38a68..297d213b31c 100644 --- a/compiler/testData/diagnostics/tests/Underscore.kt +++ b/compiler/testData/diagnostics/tests/Underscore.kt @@ -44,3 +44,7 @@ fun doIt(f: (Any?) -> Any?) = f(null) val something = doIt { __ -> __ } val something2 = doIt { _ -> 1 } + +var p: Int? + get() = null + set(_) {} diff --git a/compiler/testData/diagnostics/tests/UnderscoreUsageInCall.fir.kt b/compiler/testData/diagnostics/tests/UnderscoreUsageInCall.fir.kt deleted file mode 100644 index 7c60cce7fc8..00000000000 --- a/compiler/testData/diagnostics/tests/UnderscoreUsageInCall.fir.kt +++ /dev/null @@ -1,42 +0,0 @@ -// !DIAGNOSTICS: -DEPRECATION -TOPLEVEL_TYPEALIASES_ONLY - -fun test(`_`: Int) { - _ + 1 - `_` + 1 -} - -fun `__`() {} - -fun testCall() { - __() - `__`() -} - -val testCallableRef = ::__ -val testCallableRef2 = ::`__` - - -object Host { - val `_` = 42 - object `__` { - val bar = 4 - } -} - -val testQualified = Host._ -val testQualified2 = Host.`_` - -object `___` { - val test = 42 -} - -val testQualifier = ___.test -val testQualifier2 = `___`.test -val testQualifier3 = Host.__.bar -val testQualifier4 = Host.`__`.bar - -fun testCallableRefLHSValue(`_`: Any) = _::toString -fun testCallableRefLHSValue2(`_`: Any) = `_`::toString - -val testCallableRefLHSObject = ___::toString -val testCallableRefLHSObject2 = `___`::toString diff --git a/compiler/testData/diagnostics/tests/UnderscoreUsageInCall.kt b/compiler/testData/diagnostics/tests/UnderscoreUsageInCall.kt index fb91a56dbc7..22e0ae102e6 100644 --- a/compiler/testData/diagnostics/tests/UnderscoreUsageInCall.kt +++ b/compiler/testData/diagnostics/tests/UnderscoreUsageInCall.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -DEPRECATION -TOPLEVEL_TYPEALIASES_ONLY fun test(`_`: Int) { diff --git a/compiler/testData/diagnostics/tests/UnderscoreUsageInCallableRefTypeLHS.fir.kt b/compiler/testData/diagnostics/tests/UnderscoreUsageInCallableRefTypeLHS.fir.kt deleted file mode 100644 index e97cd0f103f..00000000000 --- a/compiler/testData/diagnostics/tests/UnderscoreUsageInCallableRefTypeLHS.fir.kt +++ /dev/null @@ -1,11 +0,0 @@ -class `___` { - class `____` -} - -val testCallableRefLHSType = ___::toString -val testCallableRefLHSType2 = `___`::toString - -val testClassLiteralLHSType = ___::class -val testClassLiteralLHSType2 = `___`::class - -val tesLHSTypeFQN = `___`.____::class \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/UnderscoreUsageInCallableRefTypeLHS.kt b/compiler/testData/diagnostics/tests/UnderscoreUsageInCallableRefTypeLHS.kt index 17b63f0d9a4..aa3c043fe0a 100644 --- a/compiler/testData/diagnostics/tests/UnderscoreUsageInCallableRefTypeLHS.kt +++ b/compiler/testData/diagnostics/tests/UnderscoreUsageInCallableRefTypeLHS.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL class `___` { class `____` } diff --git a/compiler/testData/diagnostics/tests/UnderscoreUsageInType.fir.kt b/compiler/testData/diagnostics/tests/UnderscoreUsageInType.fir.kt deleted file mode 100644 index 181bc7a41cb..00000000000 --- a/compiler/testData/diagnostics/tests/UnderscoreUsageInType.fir.kt +++ /dev/null @@ -1,12 +0,0 @@ -// !DIAGNOSTICS: -DEPRECATION -TOPLEVEL_TYPEALIASES_ONLY - -class `_`<`__`> { - fun testTypeArgument(x: List<__>) = x - fun testTypeArgument2(x: List<`__`>) = x -} - -fun _.testTypeConstructor() {} -fun `_`.testTypeConstructor2() {} - -val testConstructor = _() -val testConstructor2 = `_`() diff --git a/compiler/testData/diagnostics/tests/UnderscoreUsageInType.kt b/compiler/testData/diagnostics/tests/UnderscoreUsageInType.kt index 2c27b544f1f..42a229d7b15 100644 --- a/compiler/testData/diagnostics/tests/UnderscoreUsageInType.kt +++ b/compiler/testData/diagnostics/tests/UnderscoreUsageInType.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -DEPRECATION -TOPLEVEL_TYPEALIASES_ONLY class `_`<`__`> { diff --git a/compiler/testData/diagnostics/tests/UnderscoreUsageInVariableAsFunctionCall.fir.kt b/compiler/testData/diagnostics/tests/UnderscoreUsageInVariableAsFunctionCall.fir.kt deleted file mode 100644 index 84c1dc55f57..00000000000 --- a/compiler/testData/diagnostics/tests/UnderscoreUsageInVariableAsFunctionCall.fir.kt +++ /dev/null @@ -1,6 +0,0 @@ -object Host { - val `____` = { -> } - fun testFunTypeVal() { - ____() - } -} diff --git a/compiler/testData/diagnostics/tests/UnderscoreUsageInVariableAsFunctionCall.kt b/compiler/testData/diagnostics/tests/UnderscoreUsageInVariableAsFunctionCall.kt index 36809b2a6f2..eb7488e20e3 100644 --- a/compiler/testData/diagnostics/tests/UnderscoreUsageInVariableAsFunctionCall.kt +++ b/compiler/testData/diagnostics/tests/UnderscoreUsageInVariableAsFunctionCall.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL object Host { val `____` = { -> } fun testFunTypeVal() { diff --git a/compiler/testData/diagnostics/tests/declarationChecks/destructuringDeclarations/underscore.fir.kt b/compiler/testData/diagnostics/tests/declarationChecks/destructuringDeclarations/underscore.fir.kt index 9d6e1135d55..9e0f87e8786 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/destructuringDeclarations/underscore.fir.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/destructuringDeclarations/underscore.fir.kt @@ -11,39 +11,39 @@ class C { fun test() { for ((x, _) in C()) { - foo(x, _) + foo(x, _) } for ((_, y) in C()) { - foo(_, y) + foo(_, y) } for ((_, _) in C()) { - foo(_, _) + foo(_, _) } for ((_ : Int, _ : String) in C()) { - foo(_, _) + foo(_, _) } for ((_ : String, _ : Int) in C()) { - foo(_, _) + foo(_, _) } val (x, _) = A() val (_, y) = A() foo(x, y) - foo(x, _) - foo(_, y) + foo(x, _) + foo(_, y) val (`_`, z) = A() - foo(_, z) + foo(_, z) val (_, `_`) = A() - foo(_, y) + foo(_, y) val (unused, _) = A() } diff --git a/compiler/testData/diagnostics/tests/resolve/underscoreInCatchBlock.fir.kt b/compiler/testData/diagnostics/tests/resolve/underscoreInCatchBlock.fir.kt index 805c7ebac3c..3e8db42e491 100644 --- a/compiler/testData/diagnostics/tests/resolve/underscoreInCatchBlock.fir.kt +++ b/compiler/testData/diagnostics/tests/resolve/underscoreInCatchBlock.fir.kt @@ -29,7 +29,7 @@ fun foo() { } } catch (_: Exception) { `_`.stackTrace - val y1 = _ + val y1 = _ val y2 = (`_`) } try { diff --git a/compiler/testData/diagnostics/tests/resolve/underscoreInCatchBlockWithEnabledFeature.fir.kt b/compiler/testData/diagnostics/tests/resolve/underscoreInCatchBlockWithEnabledFeature.fir.kt index 1bb2a00e099..ebce02da3ea 100644 --- a/compiler/testData/diagnostics/tests/resolve/underscoreInCatchBlockWithEnabledFeature.fir.kt +++ b/compiler/testData/diagnostics/tests/resolve/underscoreInCatchBlockWithEnabledFeature.fir.kt @@ -29,7 +29,7 @@ fun foo() { } } catch (_: Exception) { `_`.stackTrace - val y1 = _ + val y1 = _ val y2 = (`_`) } try { diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt index 69381817217..d6c2c2ab951 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/handlers/FirDiagnosticsHandler.kt @@ -9,6 +9,7 @@ import com.intellij.psi.PsiElement import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory1 import org.jetbrains.kotlin.checkers.utils.TypeOfCall +import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.rendering.Renderers import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.analysis.diagnostics.* @@ -32,6 +33,7 @@ import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.resolve.AnalyzingUtils +import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives import org.jetbrains.kotlin.test.directives.model.DirectivesContainer @@ -70,7 +72,10 @@ class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(tes for (file in module.files) { val firFile = info.firFiles[file] ?: continue - val diagnostics = diagnosticsPerFile[firFile] ?: continue + var diagnostics = diagnosticsPerFile[firFile] ?: continue + if (AdditionalFilesDirectives.CHECK_TYPE in module.directives) { + diagnostics = diagnostics.filter { it.factory.name != Errors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.name } + } val diagnosticsMetadataInfos = diagnostics.mapNotNull { diagnostic -> if (!diagnosticsService.shouldRenderDiagnostic(module, diagnostic.factory.name)) return@mapNotNull null // SYNTAX errors will be reported later diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/constant-literals/integer-literals/decimal-integer-literals/p-1/neg/2.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/constant-literals/integer-literals/decimal-integer-literals/p-1/neg/2.1.kt index 3bbedc51d33..d4377d7b4ef 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/constant-literals/integer-literals/decimal-integer-literals/p-1/neg/2.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/constant-literals/integer-literals/decimal-integer-literals/p-1/neg/2.1.kt @@ -21,10 +21,10 @@ val value_3 = _____________0000 val value_4 = _______________________________________________________________________________________________________________________________________________________0 // TESTCASE NUMBER: 5 -val value_5 = ____________________________________________________ +val value_5 = ____________________________________________________ // TESTCASE NUMBER: 6 -val value_6 = _ +val value_6 = _ // TESTCASE NUMBER: 7 val value_7 = _0_ diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/constant-literals/integer-literals/decimal-integer-literals/p-1/neg/2.2.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/constant-literals/integer-literals/decimal-integer-literals/p-1/neg/2.2.kt index fb61c49e215..ddd79b13b1c 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/constant-literals/integer-literals/decimal-integer-literals/p-1/neg/2.2.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/constant-literals/integer-literals/decimal-integer-literals/p-1/neg/2.2.kt @@ -21,10 +21,10 @@ val value_3 = _____________0000 val value_4 = _______________________________________________________________________________________________________________________________________________________0 // TESTCASE NUMBER: 5 -val value_5 = ____________________________________________________ +val value_5 = ____________________________________________________ // TESTCASE NUMBER: 6 -val value_6 = _ +val value_6 = _ // TESTCASE NUMBER: 7 val value_7 = _0_ diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/constant-literals/integer-literals/decimal-integer-literals/p-1/neg/2.3.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/constant-literals/integer-literals/decimal-integer-literals/p-1/neg/2.3.kt index e359503bf8a..7edf14d2851 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/constant-literals/integer-literals/decimal-integer-literals/p-1/neg/2.3.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/constant-literals/integer-literals/decimal-integer-literals/p-1/neg/2.3.kt @@ -21,10 +21,10 @@ val value_3 = _____________0000 val value_4 = _______________________________________________________________________________________________________________________________________________________0 // TESTCASE NUMBER: 5 -val value_5 = ____________________________________________________ +val value_5 = ____________________________________________________ // TESTCASE NUMBER: 6 -val value_6 = _ +val value_6 = _ // TESTCASE NUMBER: 7 val value_7 = _0_ 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 f59b53bd7ea..f1a991b2f2c 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 @@ -1960,6 +1960,18 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.UNDERSCORE_IS_RESERVED) { firDiagnostic -> + UnderscoreIsReservedImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } + add(FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS) { firDiagnostic -> + UnderscoreUsageWithoutBackticksImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.TOPLEVEL_TYPEALIASES_ONLY) { firDiagnostic -> ToplevelTypealiasesOnlyImpl( firDiagnostic as FirPsiDiagnostic<*>, 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 5d23f6d9195..b4da34f311a 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 @@ -1371,6 +1371,14 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val candidates: List } + abstract class UnderscoreIsReserved : KtFirDiagnostic() { + override val diagnosticClass get() = UnderscoreIsReserved::class + } + + abstract class UnderscoreUsageWithoutBackticks : KtFirDiagnostic() { + override val diagnosticClass get() = UnderscoreUsageWithoutBackticks::class + } + abstract class ToplevelTypealiasesOnly : KtFirDiagnostic() { override val diagnosticClass get() = ToplevelTypealiasesOnly::class } 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 b2adfa8560e..5c254c428d3 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 @@ -2224,6 +2224,20 @@ internal class DelegateSpecialFunctionNoneApplicableImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class UnderscoreIsReservedImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.UnderscoreIsReserved(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + +internal class UnderscoreUsageWithoutBackticksImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.UnderscoreUsageWithoutBackticks(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class ToplevelTypealiasesOnlyImpl( firDiagnostic: FirPsiDiagnostic<*>, override val token: ValidityToken,