diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/AbstractFirPropertyInitializationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/AbstractFirPropertyInitializationChecker.kt index 37249d3717e..e4683484ef7 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/AbstractFirPropertyInitializationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/AbstractFirPropertyInitializationChecker.kt @@ -5,12 +5,9 @@ package org.jetbrains.kotlin.fir.analysis.cfa -import kotlinx.collections.immutable.PersistentMap -import kotlinx.collections.immutable.persistentMapOf -import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter -import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference -import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol abstract class AbstractFirPropertyInitializationChecker { @@ -20,89 +17,4 @@ abstract class AbstractFirPropertyInitializationChecker { data: Map, PropertyInitializationInfo>, properties: Set ) - - class PropertyInitializationInfo( - map: PersistentMap = persistentMapOf() - ) : ControlFlowInfo(map) { - companion object { - val EMPTY = PropertyInitializationInfo() - } - - override val constructor: (PersistentMap) -> PropertyInitializationInfo = - ::PropertyInitializationInfo - - fun merge(other: PropertyInitializationInfo): PropertyInitializationInfo { - var result = this - for (symbol in keys.union(other.keys)) { - val kind1 = this[symbol] ?: EventOccurrencesRange.ZERO - val kind2 = other[symbol] ?: EventOccurrencesRange.ZERO - result = result.put(symbol, kind1 or kind2) - } - return result - } - } - - class LocalPropertyCollector private constructor() : ControlFlowGraphVisitorVoid() { - companion object { - fun collect(graph: ControlFlowGraph): MutableSet { - val collector = LocalPropertyCollector() - graph.traverse(TraverseDirection.Forward, collector) - return collector.symbols - } - } - - private val symbols: MutableSet = mutableSetOf() - - override fun visitNode(node: CFGNode<*>) {} - - override fun visitVariableDeclarationNode(node: VariableDeclarationNode) { - symbols += node.fir.symbol - } - } - - class DataCollector(private val localProperties: Set) : - ControlFlowGraphVisitor>() { - override fun visitNode(node: CFGNode<*>, data: Collection): PropertyInitializationInfo { - if (data.isEmpty()) return PropertyInitializationInfo.EMPTY - return data.reduce(PropertyInitializationInfo::merge) - } - - override fun visitVariableAssignmentNode( - node: VariableAssignmentNode, - data: Collection - ): PropertyInitializationInfo { - val dataForNode = visitNode(node, data) - val reference = node.fir.lValue as? FirResolvedNamedReference ?: return dataForNode - val symbol = reference.resolvedSymbol as? FirPropertySymbol ?: return dataForNode - return if (symbol !in localProperties) { - dataForNode - } else { - processVariableWithAssignment(dataForNode, symbol) - } - } - - override fun visitVariableDeclarationNode( - node: VariableDeclarationNode, - data: Collection - ): PropertyInitializationInfo { - val dataForNode = visitNode(node, data) - return if (node.fir.initializer == null && node.fir.delegate == null) { - dataForNode - } else { - processVariableWithAssignment(dataForNode, node.fir.symbol) - } - } - - fun getData(graph: ControlFlowGraph) = - graph.collectDataForNode(TraverseDirection.Forward, PropertyInitializationInfo.EMPTY, DataCollector(localProperties)) - - private fun processVariableWithAssignment( - dataForNode: PropertyInitializationInfo, - symbol: FirPropertySymbol - ): PropertyInitializationInfo { - val existingKind = dataForNode[symbol] ?: EventOccurrencesRange.ZERO - val kind = existingKind + EventOccurrencesRange.EXACTLY_ONCE - return dataForNode.put(symbol, kind) - } - } } \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/CfaUtils.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/CfaUtils.kt new file mode 100644 index 00000000000..00180f9efd6 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/CfaUtils.kt @@ -0,0 +1,103 @@ +/* + * Copyright 2010-2020 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.cfa + +import kotlinx.collections.immutable.PersistentMap +import kotlinx.collections.immutable.persistentMapOf +import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange +import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol + + +class PropertyInitializationInfo( + map: PersistentMap = persistentMapOf() +) : ControlFlowInfo(map) { + companion object { + val EMPTY = PropertyInitializationInfo() + } + + override val constructor: (PersistentMap) -> PropertyInitializationInfo = + ::PropertyInitializationInfo + + fun merge(other: PropertyInitializationInfo): PropertyInitializationInfo { + var result = this + for (symbol in keys.union(other.keys)) { + val kind1 = this[symbol] ?: EventOccurrencesRange.ZERO + val kind2 = other[symbol] ?: EventOccurrencesRange.ZERO + result = result.put(symbol, kind1 or kind2) + } + return result + } +} + +class LocalPropertyCollector private constructor() : ControlFlowGraphVisitorVoid() { + companion object { + fun collect(graph: ControlFlowGraph): MutableSet { + val collector = LocalPropertyCollector() + graph.traverse(TraverseDirection.Forward, collector) + return collector.symbols + } + } + + private val symbols: MutableSet = mutableSetOf() + + override fun visitNode(node: CFGNode<*>) {} + + override fun visitVariableDeclarationNode(node: VariableDeclarationNode) { + symbols += node.fir.symbol + } +} + +class PropertyInitializationInfoCollector(private val localProperties: Set) : + ControlFlowGraphVisitor>() { + override fun visitNode(node: CFGNode<*>, data: Collection): PropertyInitializationInfo { + if (data.isEmpty()) return PropertyInitializationInfo.EMPTY + return data.reduce(PropertyInitializationInfo::merge) + } + + override fun visitVariableAssignmentNode( + node: VariableAssignmentNode, + data: Collection + ): PropertyInitializationInfo { + val dataForNode = visitNode(node, data) + val reference = node.fir.lValue as? FirResolvedNamedReference ?: return dataForNode + val symbol = reference.resolvedSymbol as? FirPropertySymbol ?: return dataForNode + return if (symbol !in localProperties) { + dataForNode + } else { + processVariableWithAssignment(dataForNode, symbol) + } + } + + override fun visitVariableDeclarationNode( + node: VariableDeclarationNode, + data: Collection + ): PropertyInitializationInfo { + val dataForNode = visitNode(node, data) + return if (node.fir.initializer == null && node.fir.delegate == null) { + dataForNode + } else { + processVariableWithAssignment(dataForNode, node.fir.symbol) + } + } + + fun getData(graph: ControlFlowGraph) = + graph.collectDataForNode( + TraverseDirection.Forward, + PropertyInitializationInfo.EMPTY, + this + ) + + private fun processVariableWithAssignment( + dataForNode: PropertyInitializationInfo, + symbol: FirPropertySymbol + ): PropertyInitializationInfo { + val existingKind = dataForNode[symbol] ?: EventOccurrencesRange.ZERO + val kind = existingKind + EventOccurrencesRange.EXACTLY_ONCE + return dataForNode.put(symbol, kind) + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirControlFlowAnalyzer.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirControlFlowAnalyzer.kt index 3e891e96536..2a6fbc30aee 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirControlFlowAnalyzer.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirControlFlowAnalyzer.kt @@ -47,9 +47,9 @@ class FirControlFlowAnalyzer(session: FirSession) { } private fun runAssignmentCfaCheckers(graph: ControlFlowGraph, reporter: DiagnosticReporter) { - val properties = AbstractFirCfaPropertyAssignmentChecker.LocalPropertyCollector.collect(graph) + val properties = LocalPropertyCollector.collect(graph) if (properties.isEmpty()) return - val data = AbstractFirCfaPropertyAssignmentChecker.DataCollector(properties).getData(graph) + val data = PropertyInitializationInfoCollector(properties).getData(graph) variableAssignmentCheckers.forEach { it.analyze(graph, reporter, data, properties) } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSealedClassConstructorCallChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSealedClassConstructorCallChecker.kt index 41dfd7c9df8..d6610c355e0 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSealedClassConstructorCallChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirSealedClassConstructorCallChecker.kt @@ -23,8 +23,8 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.utils.addToStdlib.safeAs object FirSealedClassConstructorCallChecker : FirQualifiedAccessChecker() { - override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { - val constructorFir = functionCall.calleeReference.safeAs() + override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + val constructorFir = expression.calleeReference.safeAs() ?.resolvedSymbol ?.fir.safeAs() ?: return @@ -39,7 +39,7 @@ object FirSealedClassConstructorCallChecker : FirQualifiedAccessChecker() { ?: return if (typeFir.status.modality == Modality.SEALED) { - reporter.report(functionCall.calleeReference.source) + reporter.report(expression.calleeReference.source) } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAbstractSuperCallChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAbstractSuperCallChecker.kt index 02dfd4a8daf..1b028fc90b7 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAbstractSuperCallChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirAbstractSuperCallChecker.kt @@ -21,9 +21,9 @@ import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.utils.addToStdlib.safeAs object FirAbstractSuperCallChecker : FirQualifiedAccessChecker() { - override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { // require the receiver to be the super reference - functionCall.explicitReceiver.safeAs() + expression.explicitReceiver.safeAs() ?.calleeReference.safeAs() ?: return @@ -32,7 +32,7 @@ object FirAbstractSuperCallChecker : FirQualifiedAccessChecker() { if (closestClass.classKind == ClassKind.CLASS) { // handles all the FirSimpleFunction/FirProperty/etc. - val item = functionCall.getDeclaration>() + val item = expression.getDeclaration>() ?: return val declaration = item.getContainingClass(context).safeAs() @@ -42,7 +42,7 @@ object FirAbstractSuperCallChecker : FirQualifiedAccessChecker() { declaration.modality == Modality.ABSTRACT && item.modality == Modality.ABSTRACT ) { - reporter.report(functionCall.calleeReference.source) + reporter.report(expression.calleeReference.source) } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExpressionChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExpressionChecker.kt index 42727c0de00..2aa1bf83654 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExpressionChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirExpressionChecker.kt @@ -13,7 +13,7 @@ import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment abstract class FirExpressionChecker { - abstract fun check(functionCall: E, context: CheckerContext, reporter: DiagnosticReporter) + abstract fun check(expression: E, context: CheckerContext, reporter: DiagnosticReporter) } typealias FirBasicExpresionChecker = FirExpressionChecker diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirNotASupertypeChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirNotASupertypeChecker.kt index cd4ff94dfad..9a06d7e999a 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirNotASupertypeChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirNotASupertypeChecker.kt @@ -19,8 +19,8 @@ import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.utils.addToStdlib.safeAs object FirNotASupertypeChecker : FirQualifiedAccessChecker() { - override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { - val superReference = functionCall.calleeReference.safeAs()?.takeIf { it.hadExplicitTypeInSource() } + override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + val superReference = expression.calleeReference.safeAs()?.takeIf { it.hadExplicitTypeInSource() } val targetClass = superReference ?.superTypeRef @@ -29,7 +29,7 @@ object FirNotASupertypeChecker : FirQualifiedAccessChecker() { val surrounding = context.findClosestClass(superReference.labelName) ?: return if (!targetClass.isSupertypeOf(surrounding)) { - reporter.report(functionCall.source) + reporter.report(expression.source) } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirProjectionsOnNonClassTypeArgumentChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirProjectionsOnNonClassTypeArgumentChecker.kt index 8d6671347e5..3c227fc60dd 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirProjectionsOnNonClassTypeArgumentChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirProjectionsOnNonClassTypeArgumentChecker.kt @@ -15,8 +15,8 @@ import org.jetbrains.kotlin.fir.types.FirTypeProjectionWithVariance import org.jetbrains.kotlin.types.Variance object FirProjectionsOnNonClassTypeArgumentChecker : FirQualifiedAccessChecker() { - override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { - for (it in functionCall.typeArguments) { + override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + for (it in expression.typeArguments) { when (it) { is FirStarProjection -> reporter.report(it.source) is FirTypeProjectionWithVariance -> { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirQualifiedSupertypeExtendedByOtherSupertypeChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirQualifiedSupertypeExtendedByOtherSupertypeChecker.kt index 0e75cba0538..13c2a5110ec 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirQualifiedSupertypeExtendedByOtherSupertypeChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirQualifiedSupertypeExtendedByOtherSupertypeChecker.kt @@ -20,9 +20,9 @@ import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.utils.addToStdlib.safeAs object FirQualifiedSupertypeExtendedByOtherSupertypeChecker : FirQualifiedAccessChecker() { - override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { // require to be called over a super reference - val superReference = functionCall.calleeReference.safeAs() + val superReference = expression.calleeReference.safeAs() ?.takeIf { it.hadExplicitTypeInSource() } ?: return diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuperNotAvailableChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuperNotAvailableChecker.kt index 57889e5af0b..e42b47f4d77 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuperNotAvailableChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuperNotAvailableChecker.kt @@ -16,15 +16,15 @@ import org.jetbrains.kotlin.fir.references.FirSuperReference import org.jetbrains.kotlin.utils.addToStdlib.safeAs object FirSuperNotAvailableChecker : FirQualifiedAccessChecker() { - override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { - if (functionCall.calleeReference.safeAs()?.hadExplicitTypeInSource() != true) return + override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + if (expression.calleeReference.safeAs()?.hadExplicitTypeInSource() != true) return val isInsideClass = context.containingDeclarations.any { it.source?.elementType == CLASS || it.source?.elementType == OBJECT_LITERAL } if (!isInsideClass) { - reporter.report(functionCall.source) + reporter.report(expression.source) } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuperclassNotAccessibleFromInterfaceChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuperclassNotAccessibleFromInterfaceChecker.kt index 33eb8d9fad6..82419a387ea 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuperclassNotAccessibleFromInterfaceChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuperclassNotAccessibleFromInterfaceChecker.kt @@ -21,17 +21,17 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol import org.jetbrains.kotlin.utils.addToStdlib.safeAs object FirSuperclassNotAccessibleFromInterfaceChecker : FirQualifiedAccessChecker() { - override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { val closestClass = context.findClosest() ?: return if (closestClass.classKind == ClassKind.INTERFACE) { - val origin = getClassLikeDeclaration(functionCall, context) + val origin = getClassLikeDeclaration(expression, context) ?.symbol.safeAs() ?.fir ?: return if (origin.source != null && origin.isSuperclassOf(closestClass)) { - reporter.report(functionCall.explicitReceiver?.source) + reporter.report(expression.explicitReceiver?.source) } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirTypeArgumentsNotAllowedExpressionChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirTypeArgumentsNotAllowedExpressionChecker.kt index c45208fe452..9b30629f65c 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirTypeArgumentsNotAllowedExpressionChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirTypeArgumentsNotAllowedExpressionChecker.kt @@ -23,10 +23,10 @@ import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.psi.KtTypeArgumentList object FirTypeArgumentsNotAllowedExpressionChecker : FirQualifiedAccessChecker() { - override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { // analyze type parameters near // package names - val explicitReceiver = functionCall.explicitReceiver + val explicitReceiver = expression.explicitReceiver if (explicitReceiver is FirResolvedQualifier && explicitReceiver.symbol == null) { if (explicitReceiver.source?.hasAnyArguments() == true) { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUpperBoundViolatedChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUpperBoundViolatedChecker.kt index d237593645a..b11013960b0 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUpperBoundViolatedChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirUpperBoundViolatedChecker.kt @@ -25,12 +25,12 @@ import org.jetbrains.kotlin.utils.addToStdlib.min import org.jetbrains.kotlin.utils.addToStdlib.safeAs object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() { - override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { // something that contains the type parameters // declarations with their declared bounds. // it may be the called function declaration // or the class declaration - val calleeFir = functionCall.calleeReference.safeAs() + val calleeFir = expression.calleeReference.safeAs() ?.resolvedSymbol ?.fir.safeAs() ?: return @@ -41,10 +41,10 @@ object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() { ) val parameterPairs = mutableMapOf() - val count = min(calleeFir.typeParameters.size, functionCall.typeArguments.size) + val count = min(calleeFir.typeParameters.size, expression.typeArguments.size) for (it in 0 until count) { - functionCall.typeArguments[it].safeAs() + expression.typeArguments[it].safeAs() ?.typeRef.safeAs() ?.let { that -> if (that !is FirErrorTypeRef) { @@ -92,7 +92,7 @@ object FirUpperBoundViolatedChecker : FirQualifiedAccessChecker() { // typealias A = B> // val a = A() when (calleeFir) { - is FirConstructor -> analyzeConstructorCall(functionCall, substitutor, typeCheckerContext, reporter) + is FirConstructor -> analyzeConstructorCall(expression, substitutor, typeCheckerContext, reporter) } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/ArrayEqualityCanBeReplacedWithEquals.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/ArrayEqualityCanBeReplacedWithEquals.kt index 203e257807d..5e4dea1dd66 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/ArrayEqualityCanBeReplacedWithEquals.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/ArrayEqualityCanBeReplacedWithEquals.kt @@ -20,15 +20,15 @@ import org.jetbrains.kotlin.fir.types.classId import org.jetbrains.kotlin.fir.types.coneType object ArrayEqualityCanBeReplacedWithEquals : FirBasicExpresionChecker() { - override fun check(functionCall: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { - if (functionCall !is FirEqualityOperatorCall) return - if (functionCall.operation != FirOperation.EQ && functionCall.operation != FirOperation.NOT_EQ) return - val left = functionCall.arguments.getOrNull(0) ?: return - val right = functionCall.arguments.getOrNull(1) ?: return + override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { + if (expression !is FirEqualityOperatorCall) return + if (expression.operation != FirOperation.EQ && expression.operation != FirOperation.NOT_EQ) return + val left = expression.arguments.getOrNull(0) ?: return + val right = expression.arguments.getOrNull(1) ?: return if (left.typeRef.coneType.classId != StandardClassIds.Array) return if (right.typeRef.coneType.classId != StandardClassIds.Array) return - reporter.report(functionCall.psi?.children?.get(1)?.toFirPsiSourceElement(), ARRAY_EQUALITY_OPERATOR_CAN_BE_REPLACED_WITH_EQUALS) + reporter.report(expression.psi?.children?.get(1)?.toFirPsiSourceElement(), ARRAY_EQUALITY_OPERATOR_CAN_BE_REPLACED_WITH_EQUALS) } -} +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/CanBeReplacedWithOperatorAssignmentChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/CanBeReplacedWithOperatorAssignmentChecker.kt index 6d99cb178d6..054643bb57e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/CanBeReplacedWithOperatorAssignmentChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/CanBeReplacedWithOperatorAssignmentChecker.kt @@ -7,7 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.extended import com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext -import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirExpressionChecker +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirVariableAssignmentChecker import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.expressions.FirFunctionCall @@ -24,17 +24,17 @@ import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtOperationReferenceExpression -object CanBeReplacedWithOperatorAssignmentChecker : FirExpressionChecker() { - override fun check(assignment: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) { - val lValue = assignment.lValue +object CanBeReplacedWithOperatorAssignmentChecker : FirVariableAssignmentChecker() { + override fun check(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) { + val lValue = expression.lValue if (lValue !is FirResolvedNamedReference) return - val operator = assignment.psi?.children?.getOrNull(1) ?: return + val operator = expression.psi?.children?.getOrNull(1) ?: return val operationSign = (operator as? KtOperationReferenceExpression)?.operationSignTokenType if (operationSign != KtTokens.EQ) return val lValuePsi = lValue.psi as? KtNameReferenceExpression ?: return - val rValue = assignment.rValue as? FirFunctionCall ?: return + val rValue = expression.rValue as? FirFunctionCall ?: return val rValuePsi = rValue.psi as? KtBinaryExpression ?: return val rValueClassId = rValue.explicitReceiver?.typeRef?.coneType?.classId @@ -60,11 +60,9 @@ object CanBeReplacedWithOperatorAssignmentChecker : FirExpressionChecker) return - if (functionCall !is FirFunctionCall) return - val left = functionCall.rangeLeft ?: return - val right = functionCall.rangeRight ?: return + override fun check(expression: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { + if (expression.source is FirFakeSourceElement<*>) return + if (expression !is FirFunctionCall) return + val left = expression.rangeLeft ?: return + val right = expression.rangeRight ?: return - val needReport = when (functionCall.calleeReference.name.asString()) { + val needReport = when (expression.calleeReference.name.asString()) { "rangeTo" -> { left > right } @@ -36,7 +36,7 @@ object EmptyRangeChecker : FirBasicExpresionChecker() { } if (needReport) { - reporter.report(functionCall.source, FirErrors.EMPTY_RANGE) + reporter.report(expression.source, FirErrors.EMPTY_RANGE) } } @@ -57,6 +57,7 @@ object EmptyRangeChecker : FirBasicExpresionChecker() { else (arg as? FirConstExpression<*>)?.value as? Long } + // todo: add proper integer operator calls checking (e.g. (1+2)*3 transforms to 9) private val FirIntegerOperatorCall.asLong: Long? get() { val value = (dispatchReceiver as? FirConstExpression<*>)?.value as Long? ?: return null diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantCallOfConversionMethod.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantCallOfConversionMethod.kt index 25829bcbd4d..9ec47057dc1 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantCallOfConversionMethod.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantCallOfConversionMethod.kt @@ -26,14 +26,14 @@ import org.jetbrains.kotlin.psi.KtSafeQualifiedExpression import org.jetbrains.kotlin.types.expressions.OperatorConventions object RedundantCallOfConversionMethod : FirQualifiedAccessChecker() { - override fun check(functionCall: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { - if (functionCall !is FirFunctionCall) return - if (functionCall.source?.kind == FirFakeSourceElementKind.GeneratedToStringCallOnTemplateEntry) return - val functionName = functionCall.calleeReference.name.asString() + override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { + if (expression !is FirFunctionCall) return + if (expression.source?.kind == FirFakeSourceElementKind.GeneratedToStringCallOnTemplateEntry) return + val functionName = expression.calleeReference.name.asString() val qualifiedType = targetClassMap[functionName] ?: return - if (functionCall.explicitReceiver?.isRedundant(qualifiedType) == true) { - reporter.report(functionCall.source, FirErrors.REDUNDANT_CALL_OF_CONVERSION_METHOD) + if (expression.explicitReceiver?.isRedundant(qualifiedType) == true) { + reporter.report(expression.source, FirErrors.REDUNDANT_CALL_OF_CONVERSION_METHOD) } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/VariableAssignmentChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/VariableAssignmentChecker.kt index 7d28cd13ed7..61d86631ef1 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/VariableAssignmentChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/VariableAssignmentChecker.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange import org.jetbrains.kotlin.fir.FirFakeSourceElement import org.jetbrains.kotlin.fir.FirSourceElement import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirPropertyInitializationChecker +import org.jetbrains.kotlin.fir.analysis.cfa.PropertyInitializationInfo import org.jetbrains.kotlin.fir.analysis.cfa.TraverseDirection import org.jetbrains.kotlin.fir.analysis.cfa.traverse import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter