From 8bfaa39a5cd4b428a5d8156f872d9a600c5a70a1 Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Fri, 23 Jul 2021 15:49:20 +0300 Subject: [PATCH] [FIR] Implement RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER Split underscore checkers --- .../diagnostics/FirDiagnosticsList.kt | 1 + .../fir/analysis/diagnostics/FirErrors.kt | 2 + .../checkers/CommonDeclarationCheckers.kt | 1 - .../checkers/CommonExpressionCheckers.kt | 2 +- .../analysis/checkers/FirUnderscoreHelpers.kt | 35 ++++ .../fir/analysis/checkers/SourceNavigator.kt | 74 +++++++- ...FirReservedUnderscoreDeclarationChecker.kt | 89 +++++++++ .../FirReservedUnderscoreCheckers.kt | 171 ------------------ .../expression/FirUnderscoreChecker.kt | 56 ++++++ .../diagnostics/FirDefaultErrorMessages.kt | 5 + .../diagnostics/PositioningStrategies.kt | 17 +- .../resolve/underscoreInCatchBlock.fir.kt | 18 +- .../diagnostics/KtFirDataClassConverters.kt | 7 + .../api/fir/diagnostics/KtFirDiagnostics.kt | 5 + .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 8 + 15 files changed, 297 insertions(+), 194 deletions(-) create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirUnderscoreHelpers.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirReservedUnderscoreDeclarationChecker.kt delete mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReservedUnderscoreCheckers.kt create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUnderscoreChecker.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 8011c86065a..cfe86c0e14d 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 @@ -1070,6 +1070,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) { parameter("message") } 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 ca0dec65897..013fbc24670 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 @@ -57,6 +57,7 @@ import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.KtModifierListOwner +import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter @@ -556,6 +557,7 @@ object FirErrors { val DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH by error3() 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 DANGEROUS_CHARACTERS by warning1(SourceElementPositioningStrategies.NAME_IDENTIFIER) val EQUALITY_NOT_APPLICABLE by error3() 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 28335828375..74690ebe134 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 @@ -12,7 +12,6 @@ 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.FirAnonymousFunctionParametersChecker -import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirReservedUnderscoreDeclarationChecker import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirAnonymousFunctionSyntaxChecker import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirDelegationInInterfaceSyntaxChecker import org.jetbrains.kotlin.fir.analysis.checkers.syntax.FirFunctionTypeParametersSyntaxChecker diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt index 9f6d6cdb3a0..88f28b3d79e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt @@ -16,7 +16,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { override val basicExpressionCheckers: Set get() = setOf( - FirReservedUnderscoreExpressionChecker, + FirUnderscoreChecker, FirExpressionAnnotationChecker, FirDeprecationChecker ) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirUnderscoreHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirUnderscoreHelpers.kt new file mode 100644 index 00000000000..b82b815f0d5 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirUnderscoreHelpers.kt @@ -0,0 +1,35 @@ +/* + * 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 + +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 + +fun checkUnderscoreDiagnostics( + source: FirSourceElement?, + context: CheckerContext, + reporter: DiagnosticReporter, + isExpression: Boolean +) { + if (source != null && source.kind !is FirFakeSourceElementKind) { + with(SourceNavigator.forSource(source)) { + if (source.getRawIdentifier()?.isUnderscore == true) { + reporter.reportOn( + source, + if (isExpression) FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS else FirErrors.UNDERSCORE_IS_RESERVED, + context + ) + } + } + } +} + +val CharSequence.isUnderscore: Boolean + get() = all { it == '_' } \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/SourceNavigator.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/SourceNavigator.kt index 667e9e17613..8b7f595e015 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/SourceNavigator.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/SourceNavigator.kt @@ -6,15 +6,22 @@ package org.jetbrains.kotlin.fir.analysis.checkers import com.intellij.psi.PsiElement +import com.intellij.psi.PsiNameIdentifierOwner +import com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirLightSourceElement import org.jetbrains.kotlin.fir.FirPsiSourceElement import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.analysis.diagnostics.getAncestors +import org.jetbrains.kotlin.fir.analysis.diagnostics.nameIdentifier +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol import org.jetbrains.kotlin.fir.types.FirTypeRef -import org.jetbrains.kotlin.psi.KtConstructorCalleeExpression -import org.jetbrains.kotlin.psi.KtTypeReference +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.stubs.elements.KtNameReferenceExpressionElementType +import org.jetbrains.kotlin.psi.stubs.elements.KtTypeProjectionElementType /** * Service to answer source-related questions in generic fashion. @@ -26,17 +33,27 @@ interface SourceNavigator { fun FirTypeRef.isInTypeConstraint(): Boolean + fun FirSourceElement.getRawIdentifier(): String? + + fun FirDeclaration.getRawName(): String? + + fun FirValueParameterSymbol.isCatchElementParameter(): Boolean + companion object { private val lightTreeInstance = LightTreeSourceNavigator() - fun forElement(e: FirElement): SourceNavigator = when (e.source) { + fun forElement(e: FirElement): SourceNavigator = forSource(e.source) + + fun forSource(e: FirSourceElement?): SourceNavigator = when (e) { is FirLightSourceElement -> lightTreeInstance is FirPsiSourceElement -> PsiSourceNavigator null -> lightTreeInstance //shouldn't matter } - inline fun FirElement.withNavigator(block: SourceNavigator.() -> R): R = with(forElement(this), block) + inline fun FirElement.withNavigator(block: SourceNavigator.() -> R): R = with(forSource(this.source), block) + + inline fun FirSourceElement.withNavigator(block: SourceNavigator.() -> R): R = with(forSource(this), block) } } @@ -55,16 +72,61 @@ open class LightTreeSourceNavigator : SourceNavigator { .find { it.tokenType == KtNodeTypes.TYPE_CONSTRAINT || it.tokenType == KtNodeTypes.TYPE_PARAMETER } ?.tokenType == KtNodeTypes.TYPE_CONSTRAINT } + + override fun FirSourceElement.getRawIdentifier(): String? { + val tokenType = elementType + return if (tokenType is KtNameReferenceExpressionElementType || tokenType == KtTokens.IDENTIFIER) { + lighterASTNode.toString() + } else if (tokenType is KtTypeProjectionElementType) { + lighterASTNode.getChildren(treeStructure).last().toString() + } else { + null + } + } + + override fun FirDeclaration.getRawName(): String? { + return source?.let { it.treeStructure.nameIdentifier(it.lighterASTNode)?.toString() } + } + + override fun FirValueParameterSymbol.isCatchElementParameter(): Boolean { + val localSource = source ?: return false + var parent = localSource.treeStructure.getParent(localSource.lighterASTNode) + parent?.let { parent = localSource.treeStructure.getParent(it) } + return parent?.tokenType == KtNodeTypes.CATCH + } } //by default psi tree can reuse light tree manipulations object PsiSourceNavigator : LightTreeSourceNavigator() { //Swallows incorrect casts!!! - private inline fun FirElement.psi(): P? { - val psi = (source as? FirPsiSourceElement)?.psi + private inline fun FirElement.psi(): P? = source?.psi() + + private inline fun FirSourceElement.psi(): P? { + val psi = (this as? FirPsiSourceElement)?.psi return psi as? P } override fun FirTypeRef.isInConstructorCallee(): Boolean = psi()?.parent is KtConstructorCalleeExpression + + override fun FirSourceElement.getRawIdentifier(): String? { + val psi = psi() + return if (psi is KtNameReferenceExpression) { + psi.getReferencedNameElement().node.text + } else if (psi is KtTypeProjection) { + psi.typeReference?.typeElement?.text + } else if (psi is LeafPsiElement && psi.elementType == KtTokens.IDENTIFIER) { + psi.text + } else { + null + } + } + + override fun FirDeclaration.getRawName(): String? { + return (this.psi() as? PsiNameIdentifierOwner)?.nameIdentifier?.text + } + + override fun FirValueParameterSymbol.isCatchElementParameter(): Boolean { + return source?.psi()?.parent?.parent is KtCatchClause + } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirReservedUnderscoreDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirReservedUnderscoreDeclarationChecker.kt new file mode 100644 index 00000000000..583977b0bab --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirReservedUnderscoreDeclarationChecker.kt @@ -0,0 +1,89 @@ +/* + * 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.fir.FirFakeSourceElementKind +import org.jetbrains.kotlin.fir.analysis.checkers.SourceNavigator +import org.jetbrains.kotlin.fir.analysis.checkers.checkUnderscoreDiagnostics +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.isUnderscore +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.types.FirResolvedTypeRef +import org.jetbrains.kotlin.fir.types.FirUserTypeRef + +object FirReservedUnderscoreDeclarationChecker : FirBasicDeclarationChecker() { + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + if ( + declaration is FirRegularClass || + declaration is FirTypeParameter || + declaration is FirProperty || + declaration is FirTypeAlias + ) { + reportIfUnderscore(declaration, context, reporter) + } else if (declaration is FirFunction) { + if (declaration is FirSimpleFunction) { + reportIfUnderscore(declaration, context, reporter) + } + + val isSingleUnderscoreAllowed = declaration is FirAnonymousFunction || declaration is FirPropertyAccessor + for (parameter in declaration.valueParameters) { + reportIfUnderscore( + parameter, + context, + reporter, + isSingleUnderscoreAllowed = isSingleUnderscoreAllowed + ) + } + } else if (declaration is FirFile) { + for (import in declaration.imports) { + checkUnderscoreDiagnostics(import.aliasSource, context, reporter, isExpression = false) + } + } + } + + private fun reportIfUnderscore( + declaration: FirDeclaration, + context: CheckerContext, + reporter: DiagnosticReporter, + isSingleUnderscoreAllowed: Boolean = false + ) { + val declarationSource = declaration.source + if (declarationSource != null && declarationSource.kind !is FirFakeSourceElementKind) { + with(SourceNavigator.forElement(declaration)) { + val rawName = declaration.getRawName() + if (rawName?.isUnderscore == true && !(isSingleUnderscoreAllowed && rawName == "_")) { + reporter.reportOn( + declarationSource, + FirErrors.UNDERSCORE_IS_RESERVED, + context + ) + } + } + } + + val returnOrReceiverTypeRef = when (declaration) { + is FirValueParameter -> declaration.returnTypeRef + is FirFunction -> declaration.receiverTypeRef + else -> null + } + + if (returnOrReceiverTypeRef is FirResolvedTypeRef) { + val delegatedTypeRef = returnOrReceiverTypeRef.delegatedTypeRef + if (delegatedTypeRef is FirUserTypeRef) { + for (qualifierPart in delegatedTypeRef.qualifier) { + checkUnderscoreDiagnostics(qualifierPart.source, context, reporter, isExpression = true) + + for (typeArgument in qualifierPart.typeArgumentList.typeArguments) { + checkUnderscoreDiagnostics(typeArgument.source, context, reporter, isExpression = true) + } + } + } + } + } +} \ No newline at end of file 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 deleted file mode 100644 index 65fa4ffdf84..00000000000 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirReservedUnderscoreCheckers.kt +++ /dev/null @@ -1,171 +0,0 @@ -/* - * 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.PsiNameIdentifierOwner -import com.intellij.psi.impl.source.tree.LeafPsiElement -import org.jetbrains.kotlin.fir.* -import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext -import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker -import org.jetbrains.kotlin.fir.analysis.checkers.getChildren -import org.jetbrains.kotlin.fir.analysis.diagnostics.* -import org.jetbrains.kotlin.fir.declarations.* -import org.jetbrains.kotlin.fir.diagnostics.ConeUnderscoreUsageWithoutBackticks -import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.psi.KtNameReferenceExpression -import org.jetbrains.kotlin.psi.KtTypeProjection -import org.jetbrains.kotlin.psi.stubs.elements.KtNameReferenceExpressionElementType -import org.jetbrains.kotlin.psi.stubs.elements.KtTypeProjectionElementType - -object FirReservedUnderscoreExpressionChecker : FirBasicExpressionChecker() { - override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { - when (expression) { - is FirResolvable -> { - reportIfUnderscore(expression.calleeReference.source, context, reporter, isExpression = true) - } - is FirResolvedQualifier -> { - for (reservedUnderscoreDiagnostic in expression.nonFatalDiagnostics.filterIsInstance()) { - reporter.reportOn(reservedUnderscoreDiagnostic.source, FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS, context) - } - } - } - } -} - -object FirReservedUnderscoreDeclarationChecker : FirBasicDeclarationChecker() { - override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { - if ( - declaration is FirRegularClass || - declaration is FirTypeParameter || - declaration is FirProperty || - declaration is FirTypeAlias - ) { - reportIfUnderscore(declaration, context, reporter) - } else if (declaration is FirFunction) { - if (declaration is FirSimpleFunction) { - reportIfUnderscore(declaration, context, reporter) - } - - val isSingleUnderscoreAllowed = declaration is FirAnonymousFunction || declaration is FirPropertyAccessor - for (parameter in declaration.valueParameters) { - reportIfUnderscore( - parameter, - context, - reporter, - isSingleUnderscoreAllowed = isSingleUnderscoreAllowed - ) - } - } else if (declaration is FirFile) { - for (import in declaration.imports) { - reportIfUnderscore(import.aliasSource, context, reporter, isExpression = false) - } - } - } -} - -private fun reportIfUnderscore( - declaration: FirDeclaration, - context: CheckerContext, - reporter: DiagnosticReporter, - isSingleUnderscoreAllowed: Boolean = false -) { - val declarationSource = declaration.source - val rawName = when (declarationSource) { - is FirPsiSourceElement -> - (declarationSource.psi as? PsiNameIdentifierOwner)?.nameIdentifier?.text - is FirLightSourceElement -> - declarationSource.treeStructure.nameIdentifier(declarationSource.lighterASTNode)?.toString() - else -> - null - } - - reportIfUnderscore(rawName, declarationSource, context, reporter, isSingleUnderscoreAllowed) - - val returnOrReceiverTypeRef = when (declaration) { - is FirValueParameter -> declaration.returnTypeRef - is FirFunction -> declaration.receiverTypeRef - else -> null - } - - if (returnOrReceiverTypeRef is FirResolvedTypeRef) { - val delegatedTypeRef = returnOrReceiverTypeRef.delegatedTypeRef - if (delegatedTypeRef is FirUserTypeRef) { - for (qualifierPart in delegatedTypeRef.qualifier) { - reportIfUnderscore(qualifierPart.source, context, reporter, isExpression = true) - - for (typeArgument in qualifierPart.typeArgumentList.typeArguments) { - reportIfUnderscore(typeArgument.source, context, reporter, isExpression = true) - } - } - } - } -} - -private fun reportIfUnderscore( - source: FirSourceElement?, - context: CheckerContext, - reporter: DiagnosticReporter, - isExpression: Boolean -) { - if (source != null && source.kind !is FirFakeSourceElementKind) { - var rawName: String? = null - if (source is FirPsiSourceElement) { - val psi = source.psi - rawName = if (psi is KtNameReferenceExpression) { - psi.getReferencedNameElement().node.text - } else if (psi is KtTypeProjection) { - psi.typeReference?.typeElement?.text - } else if (psi is LeafPsiElement && psi.elementType == KtTokens.IDENTIFIER) { - psi.text - } else { - null - } - } else if (source is FirLightSourceElement) { - val tokenType = source.elementType - rawName = if (tokenType is KtNameReferenceExpressionElementType || tokenType == KtTokens.IDENTIFIER) { - source.lighterASTNode.toString() - } else if (tokenType is KtTypeProjectionElementType) { - source.lighterASTNode.getChildren(source.treeStructure).last().toString() - } else { - null - } - } - - if (rawName?.isUnderscore == true) { - reporter.reportOn( - source, - if (isExpression) FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS else FirErrors.UNDERSCORE_IS_RESERVED, - context - ) - } - } -} - -private fun reportIfUnderscore( - rawName: CharSequence?, - source: FirSourceElement?, - context: CheckerContext, - reporter: DiagnosticReporter, - isSingleUnderscoreAllowed: Boolean -) { - if (source == null || rawName == null || isSingleUnderscoreAllowed && rawName == "_") { - return - } - - if (rawName.isUnderscore && source.kind !is FirFakeSourceElementKind) { - reporter.reportOn( - source, - FirErrors.UNDERSCORE_IS_RESERVED, - context - ) - } -} - -val CharSequence.isUnderscore: Boolean - get() = all { it == '_' } - diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUnderscoreChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUnderscoreChecker.kt new file mode 100644 index 00000000000..7e93c7ee694 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUnderscoreChecker.kt @@ -0,0 +1,56 @@ +/* + * 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 org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.checkUnderscoreDiagnostics +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.diagnostics.ConeUnderscoreUsageWithoutBackticks +import org.jetbrains.kotlin.fir.expressions.FirResolvable +import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier +import org.jetbrains.kotlin.fir.expressions.FirStatement +import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.fir.analysis.checkers.SourceNavigator + +object FirUnderscoreChecker : FirBasicExpressionChecker() { + private val singleUnderscore: Name = Name.identifier("_") + + override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { + when (expression) { + is FirResolvable -> { + checkUnderscoreDiagnostics(expression.calleeReference.source, context, reporter, true) + checkResolvedToUnderscoreNamedCatchParameter(expression, context, reporter) + } + is FirResolvedQualifier -> { + for (reservedUnderscoreDiagnostic in expression.nonFatalDiagnostics.filterIsInstance()) { + reporter.reportOn(reservedUnderscoreDiagnostic.source, FirErrors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS, context) + } + } + } + } + + private fun checkResolvedToUnderscoreNamedCatchParameter( + expression: FirResolvable, + context: CheckerContext, + reporter: DiagnosticReporter + ) { + val calleeReference = expression.calleeReference + val symbol = calleeReference.toResolvedCallableSymbol() + with(SourceNavigator.forElement(expression)) { + if (symbol is FirValueParameterSymbol && symbol.isCatchElementParameter() && symbol.name == singleUnderscore) { + reporter.reportOn( + calleeReference.source, + FirErrors.RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER, + 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 bc4d2a93d92..3ace01cc9aa 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 @@ -340,6 +340,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REPEATED_BOUND import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REPEATED_MODIFIER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RESERVED_MEMBER_INSIDE_INLINE_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RESOLUTION_TO_CLASSIFIER +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RESULT_TYPE_MISMATCH import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_FOR_BUILT_IN_SUSPEND import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_IN_FUNCTION_WITH_EXPRESSION_BODY @@ -1400,6 +1401,10 @@ class FirDefaultErrorMessages { UNDERSCORE_USAGE_WITHOUT_BACKTICKS, "Names _, __, ___, ... can be used only in back-ticks (`_`, `__`, `___`, ...)" ) + map.put( + RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER, + "Referencing to an underscore-named parameter is deprecated. It will be an error in a future release." + ) map.put( INVALID_CHARACTERS, "Name {0}", diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt index 5ce1bf90621..caf16c2e89a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/PositioningStrategies.kt @@ -838,12 +838,17 @@ object PositioningStrategies { } 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 + 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 DEFAULT.mark(element) } } diff --git a/compiler/testData/diagnostics/tests/resolve/underscoreInCatchBlock.fir.kt b/compiler/testData/diagnostics/tests/resolve/underscoreInCatchBlock.fir.kt index b0650b9785f..6006c50a72c 100644 --- a/compiler/testData/diagnostics/tests/resolve/underscoreInCatchBlock.fir.kt +++ b/compiler/testData/diagnostics/tests/resolve/underscoreInCatchBlock.fir.kt @@ -6,7 +6,7 @@ fun foo() { try { TODO() } catch (_: Exception) { - `_`.stackTrace + `_`.stackTrace } try { TODO() @@ -17,20 +17,20 @@ fun foo() { val x4 = { _: Int -> `_` } - `_` + `_` } - `_` + `_` 10 } - fun bar(x: Exception = `_`) {} + fun bar(x: Exception = `_`) {} class Bar(`_`: Exception = `_`) { - inner class Bar2(x: Exception = `_`) { } + inner class Bar2(x: Exception = `_`) { } } } } catch (_: Exception) { - `_`.stackTrace - val y1 = _ - val y2 = (`_`) + `_`.stackTrace + val y1 = _ + val y2 = (`_`) } try { TODO() @@ -38,7 +38,7 @@ fun foo() { try { TODO() } catch (x: Exception) { - `_`.stackTrace + `_`.stackTrace } } val boo1 = { `_`: Exception -> 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 e6717b001ce..ca79aa3e0bd 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 @@ -41,6 +41,7 @@ import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.KtModifierListOwner +import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter @@ -2876,6 +2877,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER) { firDiagnostic -> + ResolvedToUnderscoreNamedCatchParameterImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirErrors.INVALID_CHARACTERS) { firDiagnostic -> InvalidCharactersImpl( 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 3e8a0a55790..1339150ff0c 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 @@ -51,6 +51,7 @@ import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.KtModifierListOwner +import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter @@ -2011,6 +2012,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = UnderscoreUsageWithoutBackticks::class } + abstract class ResolvedToUnderscoreNamedCatchParameter : KtFirDiagnostic() { + override val diagnosticClass get() = ResolvedToUnderscoreNamedCatchParameter::class + } + abstract class InvalidCharacters : KtFirDiagnostic() { override val diagnosticClass get() = InvalidCharacters::class abstract val message: 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 0b4ab368659..81ac2ceaf34 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 @@ -53,6 +53,7 @@ import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.KtModifierListOwner +import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter @@ -3239,6 +3240,13 @@ internal class UnderscoreUsageWithoutBackticksImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class ResolvedToUnderscoreNamedCatchParameterImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.ResolvedToUnderscoreNamedCatchParameter(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class InvalidCharactersImpl( override val message: String, firDiagnostic: FirPsiDiagnostic,