[FIR] Implement RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER
Split underscore checkers
This commit is contained in:
+1
@@ -1070,6 +1070,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
|
||||
|
||||
val UNDERSCORE_IS_RESERVED by error<PsiElement>(PositioningStrategy.NAME_IDENTIFIER)
|
||||
val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error<PsiElement>(PositioningStrategy.NAME_IDENTIFIER)
|
||||
val RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER by warning<KtNameReferenceExpression>()
|
||||
val INVALID_CHARACTERS by error<KtNamedDeclaration>(PositioningStrategy.NAME_IDENTIFIER) {
|
||||
parameter<String>("message")
|
||||
}
|
||||
|
||||
@@ -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<KtExpression, String, ConeKotlinType, ConeKotlinType>()
|
||||
val UNDERSCORE_IS_RESERVED by error0<PsiElement>(SourceElementPositioningStrategies.NAME_IDENTIFIER)
|
||||
val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error0<PsiElement>(SourceElementPositioningStrategies.NAME_IDENTIFIER)
|
||||
val RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER by warning0<KtNameReferenceExpression>()
|
||||
val INVALID_CHARACTERS by error1<KtNamedDeclaration, String>(SourceElementPositioningStrategies.NAME_IDENTIFIER)
|
||||
val DANGEROUS_CHARACTERS by warning1<KtNamedDeclaration, String>(SourceElementPositioningStrategies.NAME_IDENTIFIER)
|
||||
val EQUALITY_NOT_APPLICABLE by error3<KtBinaryExpression, String, ConeKotlinType, ConeKotlinType>()
|
||||
|
||||
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
|
||||
override val basicExpressionCheckers: Set<FirBasicExpressionChecker>
|
||||
get() = setOf(
|
||||
FirReservedUnderscoreExpressionChecker,
|
||||
FirUnderscoreChecker,
|
||||
FirExpressionAnnotationChecker,
|
||||
FirDeprecationChecker
|
||||
)
|
||||
|
||||
+35
@@ -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 == '_' }
|
||||
+68
-6
@@ -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 <R> FirElement.withNavigator(block: SourceNavigator.() -> R): R = with(forElement(this), block)
|
||||
inline fun <R> FirElement.withNavigator(block: SourceNavigator.() -> R): R = with(forSource(this.source), block)
|
||||
|
||||
inline fun <R> 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 <reified P : PsiElement> FirElement.psi(): P? {
|
||||
val psi = (source as? FirPsiSourceElement)?.psi
|
||||
private inline fun <reified P : PsiElement> FirElement.psi(): P? = source?.psi()
|
||||
|
||||
private inline fun <reified P : PsiElement> FirSourceElement.psi(): P? {
|
||||
val psi = (this as? FirPsiSourceElement)?.psi
|
||||
return psi as? P
|
||||
}
|
||||
|
||||
override fun FirTypeRef.isInConstructorCallee(): Boolean = psi<KtTypeReference>()?.parent is KtConstructorCalleeExpression
|
||||
|
||||
override fun FirSourceElement.getRawIdentifier(): String? {
|
||||
val psi = psi<PsiElement>()
|
||||
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<PsiElement>()?.parent?.parent is KtCatchClause
|
||||
}
|
||||
}
|
||||
|
||||
+89
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-171
@@ -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<ConeUnderscoreUsageWithoutBackticks>()) {
|
||||
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 == '_' }
|
||||
|
||||
+56
@@ -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<ConeUnderscoreUsageWithoutBackticks>()) {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -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}",
|
||||
|
||||
@@ -838,12 +838,17 @@ object PositioningStrategies {
|
||||
}
|
||||
|
||||
val NAME_IDENTIFIER: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
|
||||
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<TextRange> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ fun foo() {
|
||||
try {
|
||||
TODO()
|
||||
} catch (_: Exception) {
|
||||
`_`.stackTrace
|
||||
<!RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER!>`_`<!>.stackTrace
|
||||
}
|
||||
try {
|
||||
TODO()
|
||||
@@ -17,20 +17,20 @@ fun foo() {
|
||||
val x4 = { _: Int ->
|
||||
`_`
|
||||
}
|
||||
`_`
|
||||
<!RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER!>`_`<!>
|
||||
}
|
||||
`_`
|
||||
<!RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER!>`_`<!>
|
||||
10
|
||||
}
|
||||
fun bar(x: Exception = `_`) {}
|
||||
fun bar(x: Exception = <!RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER!>`_`<!>) {}
|
||||
class Bar(`_`: Exception = <!UNINITIALIZED_PARAMETER!>`_`<!>) {
|
||||
inner class Bar2(x: Exception = `_`) { }
|
||||
inner class Bar2(x: Exception = <!RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER!>`_`<!>) { }
|
||||
}
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
`_`.stackTrace
|
||||
val y1 = <!UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>_<!>
|
||||
val y2 = (`_`)
|
||||
<!RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER!>`_`<!>.stackTrace
|
||||
val y1 = <!RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER, UNDERSCORE_USAGE_WITHOUT_BACKTICKS!>_<!>
|
||||
val y2 = (<!RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER!>`_`<!>)
|
||||
}
|
||||
try {
|
||||
TODO()
|
||||
@@ -38,7 +38,7 @@ fun foo() {
|
||||
try {
|
||||
TODO()
|
||||
} catch (x: Exception) {
|
||||
`_`.stackTrace
|
||||
<!RESOLVED_TO_UNDERSCORE_NAMED_CATCH_PARAMETER!>`_`<!>.stackTrace
|
||||
}
|
||||
}
|
||||
val boo1 = { `_`: Exception ->
|
||||
|
||||
+7
@@ -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,
|
||||
|
||||
+5
@@ -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<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
|
||||
override val diagnosticClass get() = UnderscoreUsageWithoutBackticks::class
|
||||
}
|
||||
|
||||
abstract class ResolvedToUnderscoreNamedCatchParameter : KtFirDiagnostic<KtNameReferenceExpression>() {
|
||||
override val diagnosticClass get() = ResolvedToUnderscoreNamedCatchParameter::class
|
||||
}
|
||||
|
||||
abstract class InvalidCharacters : KtFirDiagnostic<KtNamedDeclaration>() {
|
||||
override val diagnosticClass get() = InvalidCharacters::class
|
||||
abstract val message: String
|
||||
|
||||
+8
@@ -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<KtNameReferenceExpression> {
|
||||
override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic)
|
||||
}
|
||||
|
||||
internal class InvalidCharactersImpl(
|
||||
override val message: String,
|
||||
firDiagnostic: FirPsiDiagnostic,
|
||||
|
||||
Reference in New Issue
Block a user