[FIR] Implement UNDERSCORE_IS_RESERVED, UNDERSCORE_USAGE_WITHOUT_BACKTICKS diagnostics (psi only)
This commit is contained in:
committed by
TeamCityServer
parent
cc4adb798f
commit
ea2d9f7c0c
+1
@@ -67,6 +67,7 @@ enum class PositioningStrategy(private val strategy: String? = null) {
|
||||
FUN_MODIFIER,
|
||||
SUSPEND_MODIFIER,
|
||||
FUN_INTERFACE,
|
||||
RESERVED_UNDERSCORE,
|
||||
|
||||
;
|
||||
|
||||
|
||||
+2
@@ -696,6 +696,8 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
||||
parameter<String>("expectedFunctionSignature")
|
||||
parameter<Collection<AbstractFirBasedSymbol<*>>>("candidates")
|
||||
}
|
||||
val UNDERSCORE_IS_RESERVED by error<KtExpression>(PositioningStrategy.RESERVED_UNDERSCORE)
|
||||
val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error<KtExpression>(PositioningStrategy.RESERVED_UNDERSCORE)
|
||||
}
|
||||
|
||||
val TYPE_ALIAS by object : DiagnosticGroup("Type alias") {
|
||||
|
||||
@@ -409,6 +409,8 @@ object FirErrors {
|
||||
val DELEGATE_SPECIAL_FUNCTION_MISSING by error3<KtExpression, String, ConeKotlinType, String>()
|
||||
val DELEGATE_SPECIAL_FUNCTION_AMBIGUITY by error2<KtExpression, String, Collection<AbstractFirBasedSymbol<*>>>()
|
||||
val DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE by error2<KtExpression, String, Collection<AbstractFirBasedSymbol<*>>>()
|
||||
val UNDERSCORE_IS_RESERVED by error0<KtExpression>(SourceElementPositioningStrategies.RESERVED_UNDERSCORE)
|
||||
val UNDERSCORE_USAGE_WITHOUT_BACKTICKS by error0<KtExpression>(SourceElementPositioningStrategies.RESERVED_UNDERSCORE)
|
||||
|
||||
// Type alias
|
||||
val TOPLEVEL_TYPEALIASES_ONLY by error0<KtTypeAlias>()
|
||||
|
||||
+143
@@ -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<LeafPsiElement> { 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 == '_' }
|
||||
+3
@@ -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 =
|
||||
|
||||
+5
@@ -228,4 +228,9 @@ object SourceElementPositioningStrategies {
|
||||
LightTreePositioningStrategies.TYPE_PARAMETERS_LIST,
|
||||
PositioningStrategies.TYPE_PARAMETERS_LIST
|
||||
)
|
||||
|
||||
val RESERVED_UNDERSCORE = SourceElementPositioningStrategy(
|
||||
LightTreePositioningStrategies.RESERVED_UNDERSCORE,
|
||||
PositioningStrategies.RESERVED_UNDERSCORE
|
||||
)
|
||||
}
|
||||
|
||||
+2
@@ -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<FirBasicDeclarationChecker>
|
||||
@@ -20,6 +21,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
FirConflictsChecker,
|
||||
FirConflictingProjectionChecker,
|
||||
FirTypeConstraintsChecker,
|
||||
FirReservedUnderscoreDeclarationChecker
|
||||
)
|
||||
|
||||
override val memberDeclarationCheckers: Set<FirMemberDeclarationChecker>
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
|
||||
override val basicExpressionCheckers: Set<FirBasicExpressionChecker>
|
||||
get() = setOf(
|
||||
FirReservedUnderscoreExpressionChecker
|
||||
)
|
||||
|
||||
override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker>
|
||||
@@ -31,7 +32,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
FirTypeParameterInQualifiedAccessChecker,
|
||||
FirSealedClassConstructorCallChecker,
|
||||
FirUninitializedEnumChecker,
|
||||
FirFunInterfaceConstructorReferenceChecker,
|
||||
FirFunInterfaceConstructorReferenceChecker
|
||||
)
|
||||
|
||||
override val functionCallCheckers: Set<FirFunctionCallChecker>
|
||||
|
||||
Reference in New Issue
Block a user