FIR: use more precise checkers when possible

This commit is contained in:
Mikhail Glukhikh
2021-04-23 14:38:38 +03:00
parent 5c224ad17c
commit 683872ecbf
16 changed files with 121 additions and 69 deletions
@@ -25,6 +25,12 @@ fun main(args: Array<String>) {
alias<FirReturnExpression>("ReturnExpressionChecker") alias<FirReturnExpression>("ReturnExpressionChecker")
alias<FirBlock>("BlockChecker") alias<FirBlock>("BlockChecker")
alias<FirAnnotationCall>("AnnotationCallChecker") alias<FirAnnotationCall>("AnnotationCallChecker")
alias<FirCheckNotNullCall>("CheckNotNullCallChecker")
alias<FirElvisExpression>("ElvisExpressionChecker")
alias<FirGetClassCall>("GetClassCallChecker")
alias<FirSafeCallExpression>("SafeCallExpressionChecker")
alias<FirEqualityOperatorCall>("EqualityOperatorCallChecker")
alias<FirAnonymousFunction>("AnonymousFunctionAsExpressionChecker")
} }
val declarationPackage = "org.jetbrains.kotlin.fir.analysis.checkers.declaration" val declarationPackage = "org.jetbrains.kotlin.fir.analysis.checkers.declaration"
@@ -31,6 +31,18 @@ internal class ComposedExpressionCheckers : ExpressionCheckers() {
get() = _blockCheckers get() = _blockCheckers
override val annotationCallCheckers: Set<FirAnnotationCallChecker> override val annotationCallCheckers: Set<FirAnnotationCallChecker>
get() = _annotationCallCheckers get() = _annotationCallCheckers
override val checkNotNullCallCheckers: Set<FirCheckNotNullCallChecker>
get() = _checkNotNullCallCheckers
override val elvisExpressionCheckers: Set<FirElvisExpressionChecker>
get() = _elvisExpressionCheckers
override val getClassCallCheckers: Set<FirGetClassCallChecker>
get() = _getClassCallCheckers
override val safeCallExpressionCheckers: Set<FirSafeCallExpressionChecker>
get() = _safeCallExpressionCheckers
override val equalityOperatorCallCheckers: Set<FirEqualityOperatorCallChecker>
get() = _equalityOperatorCallCheckers
override val anonymousFunctionAsExpressionCheckers: Set<FirAnonymousFunctionAsExpressionChecker>
get() = _anonymousFunctionAsExpressionCheckers
private val _basicExpressionCheckers: MutableSet<FirBasicExpressionChecker> = mutableSetOf() private val _basicExpressionCheckers: MutableSet<FirBasicExpressionChecker> = mutableSetOf()
private val _qualifiedAccessCheckers: MutableSet<FirQualifiedAccessChecker> = mutableSetOf() private val _qualifiedAccessCheckers: MutableSet<FirQualifiedAccessChecker> = mutableSetOf()
@@ -41,6 +53,12 @@ internal class ComposedExpressionCheckers : ExpressionCheckers() {
private val _returnExpressionCheckers: MutableSet<FirReturnExpressionChecker> = mutableSetOf() private val _returnExpressionCheckers: MutableSet<FirReturnExpressionChecker> = mutableSetOf()
private val _blockCheckers: MutableSet<FirBlockChecker> = mutableSetOf() private val _blockCheckers: MutableSet<FirBlockChecker> = mutableSetOf()
private val _annotationCallCheckers: MutableSet<FirAnnotationCallChecker> = mutableSetOf() private val _annotationCallCheckers: MutableSet<FirAnnotationCallChecker> = mutableSetOf()
private val _checkNotNullCallCheckers: MutableSet<FirCheckNotNullCallChecker> = mutableSetOf()
private val _elvisExpressionCheckers: MutableSet<FirElvisExpressionChecker> = mutableSetOf()
private val _getClassCallCheckers: MutableSet<FirGetClassCallChecker> = mutableSetOf()
private val _safeCallExpressionCheckers: MutableSet<FirSafeCallExpressionChecker> = mutableSetOf()
private val _equalityOperatorCallCheckers: MutableSet<FirEqualityOperatorCallChecker> = mutableSetOf()
private val _anonymousFunctionAsExpressionCheckers: MutableSet<FirAnonymousFunctionAsExpressionChecker> = mutableSetOf()
@CheckersComponentInternal @CheckersComponentInternal
internal fun register(checkers: ExpressionCheckers) { internal fun register(checkers: ExpressionCheckers) {
@@ -53,5 +71,11 @@ internal class ComposedExpressionCheckers : ExpressionCheckers() {
_returnExpressionCheckers += checkers.returnExpressionCheckers _returnExpressionCheckers += checkers.returnExpressionCheckers
_blockCheckers += checkers.blockCheckers _blockCheckers += checkers.blockCheckers
_annotationCallCheckers += checkers.annotationCallCheckers _annotationCallCheckers += checkers.annotationCallCheckers
_checkNotNullCallCheckers += checkers.checkNotNullCallCheckers
_elvisExpressionCheckers += checkers.elvisExpressionCheckers
_getClassCallCheckers += checkers.getClassCallCheckers
_safeCallExpressionCheckers += checkers.safeCallExpressionCheckers
_equalityOperatorCallCheckers += checkers.equalityOperatorCallCheckers
_anonymousFunctionAsExpressionCheckers += checkers.anonymousFunctionAsExpressionCheckers
} }
} }
@@ -26,6 +26,12 @@ abstract class ExpressionCheckers {
open val returnExpressionCheckers: Set<FirReturnExpressionChecker> = emptySet() open val returnExpressionCheckers: Set<FirReturnExpressionChecker> = emptySet()
open val blockCheckers: Set<FirBlockChecker> = emptySet() open val blockCheckers: Set<FirBlockChecker> = emptySet()
open val annotationCallCheckers: Set<FirAnnotationCallChecker> = emptySet() open val annotationCallCheckers: Set<FirAnnotationCallChecker> = emptySet()
open val checkNotNullCallCheckers: Set<FirCheckNotNullCallChecker> = emptySet()
open val elvisExpressionCheckers: Set<FirElvisExpressionChecker> = emptySet()
open val getClassCallCheckers: Set<FirGetClassCallChecker> = emptySet()
open val safeCallExpressionCheckers: Set<FirSafeCallExpressionChecker> = emptySet()
open val equalityOperatorCallCheckers: Set<FirEqualityOperatorCallChecker> = emptySet()
open val anonymousFunctionAsExpressionCheckers: Set<FirAnonymousFunctionAsExpressionChecker> = emptySet()
@CheckersComponentInternal internal val allBasicExpressionCheckers: Set<FirBasicExpressionChecker> get() = basicExpressionCheckers @CheckersComponentInternal internal val allBasicExpressionCheckers: Set<FirBasicExpressionChecker> get() = basicExpressionCheckers
@CheckersComponentInternal internal val allQualifiedAccessCheckers: Set<FirQualifiedAccessChecker> get() = qualifiedAccessCheckers + basicExpressionCheckers @CheckersComponentInternal internal val allQualifiedAccessCheckers: Set<FirQualifiedAccessChecker> get() = qualifiedAccessCheckers + basicExpressionCheckers
@@ -36,4 +42,10 @@ abstract class ExpressionCheckers {
@CheckersComponentInternal internal val allReturnExpressionCheckers: Set<FirReturnExpressionChecker> get() = returnExpressionCheckers + basicExpressionCheckers @CheckersComponentInternal internal val allReturnExpressionCheckers: Set<FirReturnExpressionChecker> get() = returnExpressionCheckers + basicExpressionCheckers
@CheckersComponentInternal internal val allBlockCheckers: Set<FirBlockChecker> get() = blockCheckers + basicExpressionCheckers @CheckersComponentInternal internal val allBlockCheckers: Set<FirBlockChecker> get() = blockCheckers + basicExpressionCheckers
@CheckersComponentInternal internal val allAnnotationCallCheckers: Set<FirAnnotationCallChecker> get() = annotationCallCheckers + basicExpressionCheckers @CheckersComponentInternal internal val allAnnotationCallCheckers: Set<FirAnnotationCallChecker> get() = annotationCallCheckers + basicExpressionCheckers
@CheckersComponentInternal internal val allCheckNotNullCallCheckers: Set<FirCheckNotNullCallChecker> get() = checkNotNullCallCheckers + basicExpressionCheckers
@CheckersComponentInternal internal val allElvisExpressionCheckers: Set<FirElvisExpressionChecker> get() = elvisExpressionCheckers + basicExpressionCheckers
@CheckersComponentInternal internal val allGetClassCallCheckers: Set<FirGetClassCallChecker> get() = getClassCallCheckers + basicExpressionCheckers
@CheckersComponentInternal internal val allSafeCallExpressionCheckers: Set<FirSafeCallExpressionChecker> get() = safeCallExpressionCheckers + basicExpressionCheckers
@CheckersComponentInternal internal val allEqualityOperatorCallCheckers: Set<FirEqualityOperatorCallChecker> get() = equalityOperatorCallCheckers + basicExpressionCheckers
@CheckersComponentInternal internal val allAnonymousFunctionAsExpressionCheckers: Set<FirAnonymousFunctionAsExpressionChecker> get() = anonymousFunctionAsExpressionCheckers + basicExpressionCheckers
} }
@@ -10,11 +10,17 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression
* DO NOT MODIFY IT MANUALLY * DO NOT MODIFY IT MANUALLY
*/ */
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirBlock import org.jetbrains.kotlin.fir.expressions.FirBlock
import org.jetbrains.kotlin.fir.expressions.FirCheckNotNullCall
import org.jetbrains.kotlin.fir.expressions.FirElvisExpression
import org.jetbrains.kotlin.fir.expressions.FirEqualityOperatorCall
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirGetClassCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
import org.jetbrains.kotlin.fir.expressions.FirSafeCallExpression
import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.expressions.FirTryExpression import org.jetbrains.kotlin.fir.expressions.FirTryExpression
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
@@ -29,3 +35,9 @@ typealias FirWhenExpressionChecker = FirExpressionChecker<FirWhenExpression>
typealias FirReturnExpressionChecker = FirExpressionChecker<FirReturnExpression> typealias FirReturnExpressionChecker = FirExpressionChecker<FirReturnExpression>
typealias FirBlockChecker = FirExpressionChecker<FirBlock> typealias FirBlockChecker = FirExpressionChecker<FirBlock>
typealias FirAnnotationCallChecker = FirExpressionChecker<FirAnnotationCall> typealias FirAnnotationCallChecker = FirExpressionChecker<FirAnnotationCall>
typealias FirCheckNotNullCallChecker = FirExpressionChecker<FirCheckNotNullCall>
typealias FirElvisExpressionChecker = FirExpressionChecker<FirElvisExpression>
typealias FirGetClassCallChecker = FirExpressionChecker<FirGetClassCall>
typealias FirSafeCallExpressionChecker = FirExpressionChecker<FirSafeCallExpression>
typealias FirEqualityOperatorCallChecker = FirExpressionChecker<FirEqualityOperatorCall>
typealias FirAnonymousFunctionAsExpressionChecker = FirExpressionChecker<FirAnonymousFunction>
@@ -8,14 +8,9 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.* import org.jetbrains.kotlin.fir.analysis.diagnostics.*
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.toFirLightSourceElement
object FirAnonymousFunctionChecker : FirExpressionChecker<FirStatement>() { object FirAnonymousFunctionChecker : FirAnonymousFunctionAsExpressionChecker() {
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirAnonymousFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if (expression !is FirAnonymousFunction) {
return
}
for (valueParameter in expression.valueParameters) { for (valueParameter in expression.valueParameters) {
val source = valueParameter.source ?: continue val source = valueParameter.source ?: continue
if (valueParameter.defaultValue != null) { if (valueParameter.defaultValue != null) {
@@ -30,7 +25,7 @@ object FirAnonymousFunctionChecker : FirExpressionChecker<FirStatement>() {
} }
private fun checkTypeParameters( private fun checkTypeParameters(
expression: FirStatement, expression: FirAnonymousFunction,
reporter: DiagnosticReporter, reporter: DiagnosticReporter,
context: CheckerContext context: CheckerContext
) { ) {
@@ -21,9 +21,8 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.lexer.KtTokens.QUEST import org.jetbrains.kotlin.lexer.KtTokens.QUEST
object FirGetClassCallChecker : FirBasicExpressionChecker() { object FirClassLiteralChecker : FirGetClassCallChecker() {
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirGetClassCall, context: CheckerContext, reporter: DiagnosticReporter) {
if (expression !is FirGetClassCall) return
val source = expression.source ?: return val source = expression.source ?: return
if (source.kind is FirFakeSourceElementKind) return if (source.kind is FirFakeSourceElementKind) return
@@ -13,14 +13,11 @@ import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
import org.jetbrains.kotlin.fir.expressions.FirCheckNotNullCall import org.jetbrains.kotlin.fir.expressions.FirCheckNotNullCall
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
object FirCheckNotNullCallChecker : FirBasicExpressionChecker() { object FirNotNullAssertionChecker : FirCheckNotNullCallChecker() {
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirCheckNotNullCall, context: CheckerContext, reporter: DiagnosticReporter) {
if (expression !is FirCheckNotNullCall) return
val argument = expression.argumentList.arguments.singleOrNull() ?: return val argument = expression.argumentList.arguments.singleOrNull() ?: return
if (argument is FirAnonymousFunction && argument.isLambda) { if (argument is FirAnonymousFunction && argument.isLambda) {
reporter.reportOn(expression.source, FirErrors.NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION, context) reporter.reportOn(expression.source, FirErrors.NOT_NULL_ASSERTION_ON_LAMBDA_EXPRESSION, context)
@@ -10,16 +10,13 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.expressions.FirSafeCallExpression import org.jetbrains.kotlin.fir.expressions.FirSafeCallExpression
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.types.canBeNull import org.jetbrains.kotlin.fir.types.canBeNull
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isUnit import org.jetbrains.kotlin.fir.types.isUnit
object FirSafeCallExpressionChecker : FirBasicExpressionChecker() { object FirUnnecessarySafeCallChecker : FirSafeCallExpressionChecker() {
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirSafeCallExpression, context: CheckerContext, reporter: DiagnosticReporter) {
if (expression !is FirSafeCallExpression) return
val receiverType = expression.receiver.typeRef.coneType.fullyExpandedType(context.session) val receiverType = expression.receiver.typeRef.coneType.fullyExpandedType(context.session)
if (receiverType.isUnit) { if (receiverType.isUnit) {
reporter.reportOn(expression.source, FirErrors.UNEXPECTED_SAFE_CALL, context) reporter.reportOn(expression.source, FirErrors.UNEXPECTED_SAFE_CALL, context)
@@ -10,15 +10,13 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.expressions.FirElvisExpression import org.jetbrains.kotlin.fir.expressions.FirElvisExpression
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType
import org.jetbrains.kotlin.fir.types.canBeNull import org.jetbrains.kotlin.fir.types.canBeNull
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isNullLiteral import org.jetbrains.kotlin.fir.types.isNullLiteral
object FirElvisExpressionChecker : FirBasicExpressionChecker() { object FirUselessElvisChecker : FirElvisExpressionChecker() {
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirElvisExpression, context: CheckerContext, reporter: DiagnosticReporter) {
if (expression !is FirElvisExpression) return
// If the overall expression is not resolved/completed, the corresponding error will be reported separately. // If the overall expression is not resolved/completed, the corresponding error will be reported separately.
// See [FirControlFlowStatementsResolveTransformer#transformElvisExpression], // See [FirControlFlowStatementsResolveTransformer#transformElvisExpression],
// where an error type is recorded as the expression's return type. // where an error type is recorded as the expression's return type.
@@ -7,19 +7,17 @@ package org.jetbrains.kotlin.fir.analysis.checkers.extended
import org.jetbrains.kotlin.fir.FirFakeSourceElement import org.jetbrains.kotlin.fir.FirFakeSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpressionChecker import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.expressions.FirConstExpression import org.jetbrains.kotlin.fir.expressions.FirConstExpression
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.scopes.impl.FirIntegerOperatorCall import org.jetbrains.kotlin.fir.scopes.impl.FirIntegerOperatorCall
object EmptyRangeChecker : FirBasicExpressionChecker() { object EmptyRangeChecker : FirFunctionCallChecker() {
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
if (expression.source is FirFakeSourceElement<*>) return if (expression.source is FirFakeSourceElement<*>) return
if (expression !is FirFunctionCall) return
val left = expression.rangeLeft ?: return val left = expression.rangeLeft ?: return
val right = expression.rangeRight ?: return val right = expression.rangeRight ?: return
@@ -7,18 +7,16 @@ package org.jetbrains.kotlin.fir.analysis.checkers.extended
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext 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.declaration.FirSimpleFunctionChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
import org.jetbrains.kotlin.fir.types.isUnit import org.jetbrains.kotlin.fir.types.isUnit
object RedundantReturnUnitType : FirBasicDeclarationChecker() { object RedundantReturnUnitType : FirSimpleFunctionChecker() {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(declaration: FirSimpleFunction, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration !is FirSimpleFunction) return
if (declaration.body is FirSingleExpressionBlock) return if (declaration.body is FirSingleExpressionBlock) return
val returnType = declaration.returnTypeRef val returnType = declaration.returnTypeRef
if (returnType.source == null) return if (returnType.source == null) return
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.FirLightSourceElement import org.jetbrains.kotlin.fir.FirLightSourceElement
import org.jetbrains.kotlin.fir.FirPsiSourceElement import org.jetbrains.kotlin.fir.FirPsiSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpressionChecker import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker
import org.jetbrains.kotlin.fir.analysis.checkers.getChildren import org.jetbrains.kotlin.fir.analysis.checkers.getChildren
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_SINGLE_EXPRESSION_STRING_TEMPLATE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_SINGLE_EXPRESSION_STRING_TEMPLATE
@@ -25,12 +25,10 @@ import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.psi.KtStringTemplateExpression import org.jetbrains.kotlin.psi.KtStringTemplateExpression
object RedundantSingleExpressionStringTemplateChecker : FirBasicExpressionChecker() { object RedundantSingleExpressionStringTemplateChecker : FirFunctionCallChecker() {
override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
if (expression.source?.kind != FirFakeSourceElementKind.GeneratedToStringCallOnTemplateEntry) return if (expression.source?.kind != FirFakeSourceElementKind.GeneratedToStringCallOnTemplateEntry) return
if (expression !is FirFunctionCall) return if (expression.explicitReceiver?.typeRef?.coneType?.classId == StandardClassIds.String
if (
expression.explicitReceiver?.typeRef?.coneType?.classId == StandardClassIds.String
&& expression.stringParentChildrenCount() == 1 // there is no more children in original string template && expression.stringParentChildrenCount() == 1 // there is no more children in original string template
) { ) {
reporter.reportOn(expression.source, REDUNDANT_SINGLE_EXPRESSION_STRING_TEMPLATE, context) reporter.reportOn(expression.source, REDUNDANT_SINGLE_EXPRESSION_STRING_TEMPLATE, context)
@@ -39,12 +37,8 @@ object RedundantSingleExpressionStringTemplateChecker : FirBasicExpressionChecke
private fun FirStatement.stringParentChildrenCount(): Int? { private fun FirStatement.stringParentChildrenCount(): Int? {
return when (val source = source) { return when (val source = source) {
is FirPsiSourceElement<*> -> { is FirPsiSourceElement<*> -> source.psi.stringParentChildrenCount()
source.psi.stringParentChildrenCount() is FirLightSourceElement -> source.lighterASTNode.stringParentChildrenCount(source)
}
is FirLightSourceElement -> {
source.lighterASTNode.stringParentChildrenCount(source)
}
else -> null else -> null
} }
} }
@@ -22,7 +22,7 @@ class ExpressionCheckersDiagnosticComponent(
) : AbstractDiagnosticCollectorComponent(collector) { ) : AbstractDiagnosticCollectorComponent(collector) {
override fun visitAnonymousFunction(anonymousFunction: FirAnonymousFunction, data: CheckerContext) { override fun visitAnonymousFunction(anonymousFunction: FirAnonymousFunction, data: CheckerContext) {
checkers.allBasicExpressionCheckers.check(anonymousFunction, data, reporter) checkers.allAnonymousFunctionAsExpressionCheckers.check(anonymousFunction, data, reporter)
} }
override fun visitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: CheckerContext) { override fun visitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: CheckerContext) {
@@ -74,15 +74,15 @@ class ExpressionCheckersDiagnosticComponent(
} }
override fun visitCheckNotNullCall(checkNotNullCall: FirCheckNotNullCall, data: CheckerContext) { override fun visitCheckNotNullCall(checkNotNullCall: FirCheckNotNullCall, data: CheckerContext) {
checkers.allBasicExpressionCheckers.check(checkNotNullCall, data, reporter) checkers.allCheckNotNullCallCheckers.check(checkNotNullCall, data, reporter)
} }
override fun visitElvisExpression(elvisExpression: FirElvisExpression, data: CheckerContext) { override fun visitElvisExpression(elvisExpression: FirElvisExpression, data: CheckerContext) {
checkers.allBasicExpressionCheckers.check(elvisExpression, data, reporter) checkers.allElvisExpressionCheckers.check(elvisExpression, data, reporter)
} }
override fun visitSafeCallExpression(safeCallExpression: FirSafeCallExpression, data: CheckerContext) { override fun visitSafeCallExpression(safeCallExpression: FirSafeCallExpression, data: CheckerContext) {
checkers.basicExpressionCheckers.check(safeCallExpression, data, reporter) checkers.allSafeCallExpressionCheckers.check(safeCallExpression, data, reporter)
} }
override fun visitTryExpression(tryExpression: FirTryExpression, data: CheckerContext) { override fun visitTryExpression(tryExpression: FirTryExpression, data: CheckerContext) {
@@ -94,11 +94,11 @@ class ExpressionCheckersDiagnosticComponent(
} }
override fun visitGetClassCall(getClassCall: FirGetClassCall, data: CheckerContext) { override fun visitGetClassCall(getClassCall: FirGetClassCall, data: CheckerContext) {
checkers.allBasicExpressionCheckers.check(getClassCall, data, reporter) checkers.allGetClassCallCheckers.check(getClassCall, data, reporter)
} }
override fun visitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall, data: CheckerContext) { override fun visitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall, data: CheckerContext) {
checkers.allBasicExpressionCheckers.check(equalityOperatorCall, data, reporter) checkers.allEqualityOperatorCallCheckers.check(equalityOperatorCall, data, reporter)
} }
override fun visitVariableAssignment(variableAssignment: FirVariableAssignment, data: CheckerContext) { override fun visitVariableAssignment(variableAssignment: FirVariableAssignment, data: CheckerContext) {
@@ -15,11 +15,6 @@ object CommonExpressionCheckers : ExpressionCheckers() {
override val basicExpressionCheckers: Set<FirBasicExpressionChecker> override val basicExpressionCheckers: Set<FirBasicExpressionChecker>
get() = setOf( get() = setOf(
FirAnonymousFunctionChecker,
FirCheckNotNullCallChecker,
FirElvisExpressionChecker,
FirGetClassCallChecker,
FirSafeCallExpressionChecker,
) )
override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker> override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker>
@@ -71,4 +66,29 @@ object CommonExpressionCheckers : ExpressionCheckers() {
get() = setOf( get() = setOf(
FirForLoopChecker FirForLoopChecker
) )
override val checkNotNullCallCheckers: Set<FirCheckNotNullCallChecker>
get() = setOf(
FirNotNullAssertionChecker,
)
override val elvisExpressionCheckers: Set<FirElvisExpressionChecker>
get() = setOf(
FirUselessElvisChecker,
)
override val getClassCallCheckers: Set<FirGetClassCallChecker>
get() = setOf(
FirClassLiteralChecker,
)
override val safeCallExpressionCheckers: Set<FirSafeCallExpressionChecker>
get() = setOf(
FirUnnecessarySafeCallChecker,
)
override val anonymousFunctionAsExpressionCheckers: Set<FirAnonymousFunctionAsExpressionChecker>
get() = setOf(
FirAnonymousFunctionChecker,
)
} }
@@ -7,10 +7,7 @@ package org.jetbrains.kotlin.fir.checkers
import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirPropertyInitializationChecker import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirPropertyInitializationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers import org.jetbrains.kotlin.fir.analysis.checkers.declaration.*
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMemberDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.extended.* import org.jetbrains.kotlin.fir.analysis.checkers.extended.*
import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirDeclaration
@@ -18,7 +15,6 @@ object ExtendedDeclarationCheckers : DeclarationCheckers() {
override val basicDeclarationCheckers: Set<FirBasicDeclarationChecker> override val basicDeclarationCheckers: Set<FirBasicDeclarationChecker>
get() = setOf( get() = setOf(
RedundantVisibilityModifierChecker, RedundantVisibilityModifierChecker,
RedundantReturnUnitType,
) )
override val memberDeclarationCheckers: Set<FirMemberDeclarationChecker> override val memberDeclarationCheckers: Set<FirMemberDeclarationChecker>
@@ -37,4 +33,9 @@ object ExtendedDeclarationCheckers : DeclarationCheckers() {
get() = setOf( get() = setOf(
UnusedChecker, UnusedChecker,
) )
override val simpleFunctionCheckers: Set<FirSimpleFunctionChecker>
get() = setOf(
RedundantReturnUnitType,
)
} }
@@ -5,18 +5,13 @@
package org.jetbrains.kotlin.fir.checkers package org.jetbrains.kotlin.fir.checkers
import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers import org.jetbrains.kotlin.fir.analysis.checkers.expression.*
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpressionChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessChecker
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirVariableAssignmentChecker
import org.jetbrains.kotlin.fir.analysis.checkers.extended.* import org.jetbrains.kotlin.fir.analysis.checkers.extended.*
object ExtendedExpressionCheckers : ExpressionCheckers() { object ExtendedExpressionCheckers : ExpressionCheckers() {
override val basicExpressionCheckers: Set<FirBasicExpressionChecker> override val basicExpressionCheckers: Set<FirBasicExpressionChecker>
get() = setOf( get() = setOf(
ArrayEqualityCanBeReplacedWithEquals, ArrayEqualityCanBeReplacedWithEquals,
RedundantSingleExpressionStringTemplateChecker,
EmptyRangeChecker,
) )
override val variableAssignmentCheckers: Set<FirVariableAssignmentChecker> override val variableAssignmentCheckers: Set<FirVariableAssignmentChecker>
@@ -29,4 +24,10 @@ object ExtendedExpressionCheckers : ExpressionCheckers() {
RedundantCallOfConversionMethod, RedundantCallOfConversionMethod,
UselessCallOnNotNullChecker, UselessCallOnNotNullChecker,
) )
override val functionCallCheckers: Set<FirFunctionCallChecker>
get() = setOf(
EmptyRangeChecker,
RedundantSingleExpressionStringTemplateChecker,
)
} }