From 638a0d3b82c1044ffb8ccc69de7439e7f07478a3 Mon Sep 17 00:00:00 2001 From: vldf Date: Wed, 5 Aug 2020 17:39:43 +0300 Subject: [PATCH 1/4] [FIR] Checkers performance improvement and some refactoring --- .../kotlin/fir/analysis/CheckersComponent.kt | 5 +++ ...bstractFirPropertyInitializationChecker.kt | 10 ++++-- .../analysis/cfa/FirControlFlowAnalyzer.kt | 12 +++++-- .../cfa/FirPropertyInitializationAnalyzer.kt | 25 ++++++++++----- .../fir/analysis/checkers/FirHelpers.kt | 19 ++++++----- .../declaration/DeclarationCheckers.kt | 2 ++ .../ExtendedDeclarationCheckers.kt | 4 +-- ...BeReplacedWithOperatorAssignmentChecker.kt | 18 +++++++---- .../checkers/extended/EmptyRangeChecker.kt | 32 ++++++++++++++++--- .../RedundantCallOfConversionMethod.kt | 2 +- .../RedundantModalityModifierChecker.kt | 11 +++---- .../RedundantReturnUnitTypeChecker.kt | 5 ++- .../RedundantSetterParameterTypeChecker.kt | 1 + ...ntSingleExpressionStringTemplateChecker.kt | 3 +- .../extended/VariableAssignmentChecker.kt | 19 +++++------ 15 files changed, 112 insertions(+), 56 deletions(-) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/CheckersComponent.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/CheckersComponent.kt index 231c910a4a8..dd074dfd7ac 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/CheckersComponent.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/CheckersComponent.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSessionComponent +import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirCfaPropertyAssignmentChecker 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.* @@ -70,6 +71,8 @@ private class ComposedDeclarationCheckers : DeclarationCheckers() { get() = _constructorCheckers override val controlFlowAnalyserCheckers: List get() = _controlFlowAnalyserCheckers + override val variableAssignmentCfaBasedCheckers: List + get() = _variableAssignmentCfaBasedCheckers private val _fileCheckers: MutableList = mutableListOf() private val _declarationCheckers: MutableList = mutableListOf() @@ -77,6 +80,7 @@ private class ComposedDeclarationCheckers : DeclarationCheckers() { private val _regularClassCheckers: MutableList = mutableListOf() private val _constructorCheckers: MutableList = mutableListOf() private val _controlFlowAnalyserCheckers: MutableList = mutableListOf() + private val _variableAssignmentCfaBasedCheckers: MutableList = mutableListOf() fun register(checkers: DeclarationCheckers) { _fileCheckers += checkers.allFileCheckers @@ -85,6 +89,7 @@ private class ComposedDeclarationCheckers : DeclarationCheckers() { _regularClassCheckers += checkers.allRegularClassCheckers _constructorCheckers += checkers.allConstructorCheckers _controlFlowAnalyserCheckers += checkers.controlFlowAnalyserCheckers + _variableAssignmentCfaBasedCheckers += checkers.variableAssignmentCfaBasedCheckers } } 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 27ebedf4e88..021aa714038 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 @@ -8,14 +8,18 @@ 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.checkers.cfa.FirControlFlowChecker 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.symbols.impl.FirPropertySymbol -abstract class AbstractFirPropertyInitializationChecker : FirControlFlowChecker() { - abstract override fun analyze(graph: ControlFlowGraph, reporter: DiagnosticReporter) +abstract class AbstractFirCfaPropertyAssignmentChecker { + abstract fun analyze( + graph: ControlFlowGraph, + reporter: DiagnosticReporter, + data: Map, PropertyInitializationInfo>, + properties: Set + ) class PropertyInitializationInfo( map: PersistentMap = persistentMapOf() 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 a8643df6d60..0ff8ef5884d 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 @@ -15,7 +15,8 @@ import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph class FirControlFlowAnalyzer(session: FirSession) { - private val checkers = session.checkersComponent.declarationCheckers.controlFlowAnalyserCheckers + private val cfaCheckers = session.checkersComponent.declarationCheckers.controlFlowAnalyserCheckers + private val variableAssignmentCheckers = session.checkersComponent.declarationCheckers.variableAssignmentCfaBasedCheckers fun analyzeClassInitializer(klass: FirClass<*>, graph: ControlFlowGraph, context: CheckerContext, reporter: DiagnosticReporter) { if (graph.owner != null) return @@ -24,7 +25,14 @@ class FirControlFlowAnalyzer(session: FirSession) { fun analyzeFunction(function: FirFunction<*>, graph: ControlFlowGraph, context: CheckerContext, reporter: DiagnosticReporter) { if (graph.owner != null) return - checkers.forEach { it.analyze(graph, reporter) } + + cfaCheckers.forEach { it.analyze(graph, reporter) } + + val properties = AbstractFirCfaPropertyAssignmentChecker.LocalPropertyCollector.collect(graph) + if (properties.isEmpty()) return + val data = AbstractFirCfaPropertyAssignmentChecker.DataCollector(properties).getData(graph) + + variableAssignmentCheckers.forEach { it.analyze(graph, reporter, data, properties) } } fun analyzePropertyInitializer(property: FirProperty, graph: ControlFlowGraph, context: CheckerContext, reporter: DiagnosticReporter) { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt index 66de5563e93..47551c013ae 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt @@ -16,15 +16,24 @@ import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraphVisitorVoid import org.jetbrains.kotlin.fir.resolve.dfa.cfg.QualifiedAccessNode import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol -object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChecker() { - override fun analyze(graph: ControlFlowGraph, reporter: DiagnosticReporter) { - val localProperties = LocalPropertyCollector.collect(graph) - // we want to analyze only properties without initializers - localProperties.retainAll { it.fir.initializer == null && it.fir.delegate == null } - if (localProperties.isEmpty()) return - val data = DataCollector(localProperties).getData(graph) - val reporterVisitor = UninitializedPropertyReporter(data, localProperties, reporter) +object FirPropertyInitializationAnalyzer : AbstractFirCfaPropertyAssignmentChecker() { + override fun analyze( + graph: ControlFlowGraph, + reporter: DiagnosticReporter, + data: Map, PropertyInitializationInfo>, + properties: Set + ) { + // getting symbols of properties without initializers and non-variable symbols + val localData = data.filter { + val symbolFir = (it.key.fir as? FirVariableSymbol<*>)?.fir + symbolFir == null || symbolFir.initializer == null && symbolFir.delegate == null + } + + val localProperties = properties.filter { it.fir.initializer == null && it.fir.delegate == null }.toSet() + + val reporterVisitor = UninitializedPropertyReporter(localData, localProperties, reporter) graph.traverse(TraverseDirection.Forward, reporterVisitor) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt index ef20cc795de..baa726a2c6e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt @@ -27,7 +27,6 @@ import org.jetbrains.kotlin.fir.types.ConeClassLikeType import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.FirTypeRef -import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtModifierList import org.jetbrains.kotlin.psi.psiUtil.toVisibility @@ -185,7 +184,7 @@ fun FirClassLikeDeclaration<*>.followAllAlias(session: FirSession): FirClassLike * or null if no such item could be found. */ fun CheckerContext.findClosestClassOrObject(): FirClass<*>? { - for (it in containingDeclarations.reversed()) { + for (it in containingDeclarations.asReversed()) { if ( it is FirRegularClass || it is FirAnonymousObject @@ -237,20 +236,20 @@ fun FirClass<*>.modality(): Modality? { /** * returns implicit modality by FirMemberDeclaration */ -fun FirMemberDeclaration.implicitModality(context: CheckerContext): KtModifierKeywordToken { +fun FirMemberDeclaration.implicitModality(context: CheckerContext): Modality { if (this is FirRegularClass && (this.classKind == ClassKind.CLASS || this.classKind == ClassKind.OBJECT)) { - if (this.classKind == ClassKind.INTERFACE) return KtTokens.ABSTRACT_KEYWORD - return KtTokens.FINAL_KEYWORD + if (this.classKind == ClassKind.INTERFACE) return Modality.ABSTRACT + return Modality.FINAL } - val klass = context.findClosestClassOrObject() ?: return KtTokens.FINAL_KEYWORD - val modifiers = this.modifierListOrNull() ?: return KtTokens.FINAL_KEYWORD + val klass = context.findClosestClassOrObject() ?: return Modality.FINAL + val modifiers = this.modifierListOrNull() ?: return Modality.FINAL if (modifiers.hasModifier(KtTokens.OVERRIDE_KEYWORD)) { val klassModifiers = klass.modifierListOrNull() if (klassModifiers != null && klassModifiers.run { hasModifier(KtTokens.ABSTRACT_KEYWORD) || hasModifier(KtTokens.OPEN_KEYWORD) || hasModifier(KtTokens.SEALED_KEYWORD) }) { - return KtTokens.OPEN_KEYWORD + return Modality.OPEN } } @@ -259,10 +258,10 @@ fun FirMemberDeclaration.implicitModality(context: CheckerContext): KtModifierKe && klass.classKind == ClassKind.INTERFACE && !modifiers.hasModifier(KtTokens.PRIVATE_KEYWORD) ) { - return if (this.hasBody()) KtTokens.OPEN_KEYWORD else KtTokens.ABSTRACT_KEYWORD + return if (this.hasBody()) Modality.OPEN else Modality.ABSTRACT } - return KtTokens.FINAL_KEYWORD + return Modality.FINAL } private fun FirDeclaration.modifierListOrNull() = (this.source.getModifierList() as? FirPsiModifierList)?.modifierList diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DeclarationCheckers.kt index 822772cb403..e93050b625c 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DeclarationCheckers.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration +import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirCfaPropertyAssignmentChecker import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker abstract class DeclarationCheckers { @@ -18,6 +19,7 @@ abstract class DeclarationCheckers { open val regularClassCheckers: List = emptyList() open val constructorCheckers: List = emptyList() open val controlFlowAnalyserCheckers: List = emptyList() + open val variableAssignmentCfaBasedCheckers: List = emptyList() internal val allFileCheckers: List get() = fileCheckers + declarationCheckers internal val allMemberDeclarationCheckers: List get() = memberDeclarationCheckers + declarationCheckers diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/ExtendedDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/ExtendedDeclarationCheckers.kt index c23c1772aa3..f584010011f 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/ExtendedDeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/ExtendedDeclarationCheckers.kt @@ -5,7 +5,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration -import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker +import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirCfaPropertyAssignmentChecker import org.jetbrains.kotlin.fir.analysis.checkers.extended.* object ExtendedDeclarationCheckers : DeclarationCheckers() { @@ -20,7 +20,7 @@ object ExtendedDeclarationCheckers : DeclarationCheckers() { RedundantSetterParameterTypeChecker ) - override val controlFlowAnalyserCheckers: List = listOf( + override val variableAssignmentCfaBasedCheckers: List = listOf( VariableAssignmentChecker ) } \ 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 7fea895d4b2..6d99cb178d6 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 @@ -22,16 +22,19 @@ import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtNameReferenceExpression +import org.jetbrains.kotlin.psi.KtOperationReferenceExpression object CanBeReplacedWithOperatorAssignmentChecker : FirExpressionChecker() { - override fun check(functionCall: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) { - val lValue = functionCall.lValue + override fun check(assignment: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) { + val lValue = assignment.lValue if (lValue !is FirResolvedNamedReference) return - val operator = functionCall.psi?.children?.getOrNull(1) ?: return - if (operator.text != "=") return + + val operator = assignment.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 = functionCall.rValue as? FirFunctionCall ?: return + val rValue = assignment.rValue as? FirFunctionCall ?: return val rValuePsi = rValue.psi as? KtBinaryExpression ?: return val rValueClassId = rValue.explicitReceiver?.typeRef?.coneType?.classId @@ -56,9 +59,12 @@ object CanBeReplacedWithOperatorAssignmentChecker : FirExpressionChecker) return if (functionCall !is FirFunctionCall) return - val range = functionCall.psi ?: return - val left = range.children.getOrNull(0)?.text?.toLongOrNull() ?: return - val right = range.children.getOrNull(2)?.text?.toLongOrNull() ?: return + val left = functionCall.rangeLeft ?: return + val right = functionCall.rangeRight ?: return val needReport = when (functionCall.calleeReference.name.asString()) { "rangeTo" -> { @@ -41,4 +41,28 @@ object EmptyRangeChecker : FirBasicExpresionChecker() { } } + private val FirFunctionCall.rangeLeft: Long? + get() { + return if (explicitReceiver is FirIntegerOperatorCall) { + (explicitReceiver as? FirIntegerOperatorCall)?.asLong + } else { + (explicitReceiver as? FirConstExpression<*>)?.value as? Long + } + } + + private val FirFunctionCall.rangeRight: Long? + get() { + val arg = argumentList.arguments.getOrNull(0) + return if (arg is FirIntegerOperatorCall) arg.asLong + else (arg as? FirConstExpression<*>)?.value as? Long + } + + private val FirIntegerOperatorCall.asLong: Long? + get() { + val value = (dispatchReceiver as? FirConstExpression<*>)?.value as Long? ?: return null + if (this.calleeReference.name.asString() == "unaryMinus") { + return -value + } + return value + } } \ No newline at end of file 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 410afc87589..25829bcbd4d 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 @@ -37,7 +37,7 @@ object RedundantCallOfConversionMethod : FirQualifiedAccessChecker() { } } - private val targetClassMap = mapOf( + private val targetClassMap = hashMapOf( "toString" to StandardClassIds.String, "toDouble" to StandardClassIds.Double, "toFloat" to StandardClassIds.Float, diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantModalityModifierChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantModalityModifierChecker.kt index 5d74ff8fed5..51ceeb73cb3 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantModalityModifierChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantModalityModifierChecker.kt @@ -5,8 +5,8 @@ package org.jetbrains.kotlin.fir.analysis.checkers.extended -import com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.fir.FirFakeSourceElement import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMemberDeclarationChecker @@ -15,10 +15,9 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.REDUNDANT_MODALITY_MODIFIER import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration +import org.jetbrains.kotlin.fir.declarations.modality import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.toFirPsiSourceElement -import org.jetbrains.kotlin.lexer.KtModifierKeywordToken -import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.psiUtil.modalityModifier @@ -26,10 +25,9 @@ object RedundantModalityModifierChecker : FirMemberDeclarationChecker() { override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { if (declaration.source is FirFakeSourceElement<*>) return - val modalityModifier = (declaration.psi as? KtDeclaration)?.modalityModifier() ?: return - val modality = (modalityModifier as? LeafPsiElement)?.elementType as? KtModifierKeywordToken ?: return + val modality = declaration.modality ?: return if ( - modality == KtTokens.FINAL_KEYWORD + modality == Modality.FINAL && (context.containingDeclarations.last() as? FirClass<*>)?.classKind == ClassKind.INTERFACE ) return @@ -37,6 +35,7 @@ object RedundantModalityModifierChecker : FirMemberDeclarationChecker() { if (modality != implicitModality) return + val modalityModifier = (declaration.psi as? KtDeclaration)?.modalityModifier() ?: return reporter.report(modalityModifier.toFirPsiSourceElement(), REDUNDANT_MODALITY_MODIFIER) } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantReturnUnitTypeChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantReturnUnitTypeChecker.kt index e7e504c7a40..84f91968d89 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantReturnUnitTypeChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantReturnUnitTypeChecker.kt @@ -19,10 +19,9 @@ object RedundantReturnUnitType : FirBasicDeclarationChecker() { override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { if (declaration !is FirSimpleFunction) return if (declaration.body is FirSingleExpressionBlock) return - if (declaration.returnTypeRef.source == null) return - if (declaration.source is FirFakeSourceElement<*>) return - val returnType = declaration.returnTypeRef + if (returnType.source == null) return + if (declaration.source is FirFakeSourceElement<*>) return if (returnType.annotations.isNotEmpty()) return if (returnType.isUnit) { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSetterParameterTypeChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSetterParameterTypeChecker.kt index a68b158051a..90d49246675 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSetterParameterTypeChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSetterParameterTypeChecker.kt @@ -21,6 +21,7 @@ object RedundantSetterParameterTypeChecker : FirMemberDeclarationChecker() { val valueParameter = setter.valueParameters.firstOrNull() ?: return val propertyTypeSource = declaration.returnTypeRef.source val setterParameterTypeSource = valueParameter.returnTypeRef.source ?: return + if (setterParameterTypeSource != propertyTypeSource) { reporter.report(setterParameterTypeSource, REDUNDANT_SETTER_PARAMETER_TYPE) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSingleExpressionStringTemplateChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSingleExpressionStringTemplateChecker.kt index 82e95c989ef..bffefdf308c 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSingleExpressionStringTemplateChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantSingleExpressionStringTemplateChecker.kt @@ -33,7 +33,6 @@ object RedundantSingleExpressionStringTemplateChecker : FirBasicExpresionChecker private fun PsiElement.findStringParent(): KtStringTemplateExpression? { if (this is KtStringTemplateExpression) return this - return if (this.parent != null) this.parent.findStringParent() - else null + return this.parent?.findStringParent() } } 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 a8563ca2b8c..413dadf8b62 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 @@ -9,7 +9,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.extended 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.AbstractFirCfaPropertyAssignmentChecker import org.jetbrains.kotlin.fir.analysis.cfa.TraverseDirection import org.jetbrains.kotlin.fir.analysis.cfa.traverse import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter @@ -22,16 +22,17 @@ import org.jetbrains.kotlin.fir.toFirPsiSourceElement import org.jetbrains.kotlin.psi.KtProperty -object VariableAssignmentChecker : AbstractFirPropertyInitializationChecker() { - override fun analyze(graph: ControlFlowGraph, reporter: DiagnosticReporter) { +object VariableAssignmentChecker : AbstractFirCfaPropertyAssignmentChecker() { + override fun analyze( + graph: ControlFlowGraph, + reporter: DiagnosticReporter, + data: Map, PropertyInitializationInfo>, + properties: Set + ) { val unprocessedProperties = mutableSetOf() val propertiesCharacteristics = mutableMapOf() - val localProperties = LocalPropertyCollector.collect(graph) - if (localProperties.isEmpty()) return - - val data = DataCollector(localProperties).getData(graph) - val reporterVisitor = UninitializedPropertyReporter(data, localProperties, unprocessedProperties, propertiesCharacteristics) + val reporterVisitor = UninitializedPropertyReporter(data, properties, unprocessedProperties, propertiesCharacteristics) graph.traverse(TraverseDirection.Forward, reporterVisitor) for (property in unprocessedProperties) { @@ -63,7 +64,7 @@ object VariableAssignmentChecker : AbstractFirPropertyInitializationChecker() { destructuringCanBeVal = false } lastDestructuredVariables-- - } else if (canBeVal(symbol, value) && symbol.fir.delegate == null ) { + } else if (canBeVal(symbol, value) && symbol.fir.delegate == null) { reporter.report(source, FirErrors.CAN_BE_VAL) } } From ac074384893f3aaeeaabb8d60531915dc27d12bc Mon Sep 17 00:00:00 2001 From: vldf Date: Fri, 7 Aug 2020 17:10:23 +0300 Subject: [PATCH 2/4] [FIR] Speed up CFA-checkers --- .../kotlin/fir/analysis/CheckersComponent.kt | 6 ++-- ...bstractFirPropertyInitializationChecker.kt | 2 +- .../analysis/cfa/FirControlFlowAnalyzer.kt | 29 ++++++++++++++----- .../cfa/FirPropertyInitializationAnalyzer.kt | 3 +- .../declaration/DeclarationCheckers.kt | 4 +-- .../declaration/DefaultDeclarationCheckers.kt | 6 +++- .../ExtendedDeclarationCheckers.kt | 4 +-- .../extended/VariableAssignmentChecker.kt | 6 ++-- 8 files changed, 38 insertions(+), 22 deletions(-) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/CheckersComponent.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/CheckersComponent.kt index dd074dfd7ac..6ac63c98728 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/CheckersComponent.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/CheckersComponent.kt @@ -7,7 +7,7 @@ package org.jetbrains.kotlin.fir.analysis import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSessionComponent -import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirCfaPropertyAssignmentChecker +import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirPropertyInitializationChecker 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.* @@ -71,7 +71,7 @@ private class ComposedDeclarationCheckers : DeclarationCheckers() { get() = _constructorCheckers override val controlFlowAnalyserCheckers: List get() = _controlFlowAnalyserCheckers - override val variableAssignmentCfaBasedCheckers: List + override val variableAssignmentCfaBasedCheckers: List get() = _variableAssignmentCfaBasedCheckers private val _fileCheckers: MutableList = mutableListOf() @@ -80,7 +80,7 @@ private class ComposedDeclarationCheckers : DeclarationCheckers() { private val _regularClassCheckers: MutableList = mutableListOf() private val _constructorCheckers: MutableList = mutableListOf() private val _controlFlowAnalyserCheckers: MutableList = mutableListOf() - private val _variableAssignmentCfaBasedCheckers: MutableList = mutableListOf() + private val _variableAssignmentCfaBasedCheckers: MutableList = mutableListOf() fun register(checkers: DeclarationCheckers) { _fileCheckers += checkers.allFileCheckers 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 021aa714038..37249d3717e 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 @@ -13,7 +13,7 @@ import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol -abstract class AbstractFirCfaPropertyAssignmentChecker { +abstract class AbstractFirPropertyInitializationChecker { abstract fun analyze( graph: ControlFlowGraph, reporter: DiagnosticReporter, 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 0ff8ef5884d..cea30565043 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 @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.FirFunction import org.jetbrains.kotlin.fir.declarations.FirProperty +import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph class FirControlFlowAnalyzer(session: FirSession) { @@ -20,23 +21,35 @@ class FirControlFlowAnalyzer(session: FirSession) { fun analyzeClassInitializer(klass: FirClass<*>, graph: ControlFlowGraph, context: CheckerContext, reporter: DiagnosticReporter) { if (graph.owner != null) return - checkers.forEach { it.analyze(graph, reporter) } + cfaCheckers.forEach { it.analyze(graph, reporter) } } fun analyzeFunction(function: FirFunction<*>, graph: ControlFlowGraph, context: CheckerContext, reporter: DiagnosticReporter) { if (graph.owner != null) return cfaCheckers.forEach { it.analyze(graph, reporter) } - - val properties = AbstractFirCfaPropertyAssignmentChecker.LocalPropertyCollector.collect(graph) - if (properties.isEmpty()) return - val data = AbstractFirCfaPropertyAssignmentChecker.DataCollector(properties).getData(graph) - - variableAssignmentCheckers.forEach { it.analyze(graph, reporter, data, properties) } + if (context.containingDeclarations.any { it is FirProperty || it is FirFunction<*> }) return + runAssignmentCfaCheckers(graph, reporter) } fun analyzePropertyInitializer(property: FirProperty, graph: ControlFlowGraph, context: CheckerContext, reporter: DiagnosticReporter) { if (graph.owner != null) return - checkers.forEach { it.analyze(graph, reporter) } + + cfaCheckers.forEach { it.analyze(graph, reporter) } + runAssignmentCfaCheckers(graph, reporter) + } + + fun analyzePropertyAccessor(accessor: FirPropertyAccessor, graph: ControlFlowGraph, context: CheckerContext, reporter: DiagnosticReporter) { + if (graph.owner != null) return + + cfaCheckers.forEach { it.analyze(graph, reporter) } + runAssignmentCfaCheckers(graph, reporter) + } + + private fun runAssignmentCfaCheckers(graph: ControlFlowGraph, reporter: DiagnosticReporter) { + val properties = AbstractFirPropertyInitializationChecker.LocalPropertyCollector.collect(graph) + if (properties.isEmpty()) return + val data = AbstractFirPropertyInitializationChecker.DataCollector(properties).getData(graph) + variableAssignmentCheckers.forEach { it.analyze(graph, reporter, data, properties) } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt index 47551c013ae..c05c94b6ec1 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt @@ -18,14 +18,13 @@ import org.jetbrains.kotlin.fir.resolve.dfa.cfg.QualifiedAccessNode import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol -object FirPropertyInitializationAnalyzer : AbstractFirCfaPropertyAssignmentChecker() { +object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChecker() { override fun analyze( graph: ControlFlowGraph, reporter: DiagnosticReporter, data: Map, PropertyInitializationInfo>, properties: Set ) { - // getting symbols of properties without initializers and non-variable symbols val localData = data.filter { val symbolFir = (it.key.fir as? FirVariableSymbol<*>)?.fir symbolFir == null || symbolFir.initializer == null && symbolFir.delegate == null diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DeclarationCheckers.kt index e93050b625c..09ebff60267 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DeclarationCheckers.kt @@ -5,7 +5,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration -import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirCfaPropertyAssignmentChecker +import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirPropertyInitializationChecker import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker abstract class DeclarationCheckers { @@ -19,7 +19,7 @@ abstract class DeclarationCheckers { open val regularClassCheckers: List = emptyList() open val constructorCheckers: List = emptyList() open val controlFlowAnalyserCheckers: List = emptyList() - open val variableAssignmentCfaBasedCheckers: List = emptyList() + open val variableAssignmentCfaBasedCheckers: List = emptyList() internal val allFileCheckers: List get() = fileCheckers + declarationCheckers internal val allMemberDeclarationCheckers: List get() = memberDeclarationCheckers + declarationCheckers diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DefaultDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DefaultDeclarationCheckers.kt index d45d148d4ae..94afcb0dcb2 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DefaultDeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DefaultDeclarationCheckers.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration +import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirPropertyInitializationChecker import org.jetbrains.kotlin.fir.analysis.cfa.FirCallsEffectAnalyzer import org.jetbrains.kotlin.fir.analysis.cfa.FirPropertyInitializationAnalyzer import org.jetbrains.kotlin.fir.analysis.cfa.FirReturnsImpliesAnalyzer @@ -49,8 +50,11 @@ object CommonDeclarationCheckers : DeclarationCheckers() { ) override val controlFlowAnalyserCheckers: List = listOf( - FirPropertyInitializationAnalyzer, FirCallsEffectAnalyzer, FirReturnsImpliesAnalyzer ) + + override val variableAssignmentCfaBasedCheckers: List = listOf( + FirPropertyInitializationAnalyzer + ) } \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/ExtendedDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/ExtendedDeclarationCheckers.kt index f584010011f..2d49c266293 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/ExtendedDeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/ExtendedDeclarationCheckers.kt @@ -5,7 +5,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration -import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirCfaPropertyAssignmentChecker +import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirPropertyInitializationChecker import org.jetbrains.kotlin.fir.analysis.checkers.extended.* object ExtendedDeclarationCheckers : DeclarationCheckers() { @@ -20,7 +20,7 @@ object ExtendedDeclarationCheckers : DeclarationCheckers() { RedundantSetterParameterTypeChecker ) - override val variableAssignmentCfaBasedCheckers: List = listOf( + override val variableAssignmentCfaBasedCheckers: List = listOf( VariableAssignmentChecker ) } \ No newline at end of file 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 413dadf8b62..7d28cd13ed7 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 @@ -9,7 +9,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.extended 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.AbstractFirCfaPropertyAssignmentChecker +import org.jetbrains.kotlin.fir.analysis.cfa.AbstractFirPropertyInitializationChecker import org.jetbrains.kotlin.fir.analysis.cfa.TraverseDirection import org.jetbrains.kotlin.fir.analysis.cfa.traverse import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter @@ -22,7 +22,7 @@ import org.jetbrains.kotlin.fir.toFirPsiSourceElement import org.jetbrains.kotlin.psi.KtProperty -object VariableAssignmentChecker : AbstractFirCfaPropertyAssignmentChecker() { +object VariableAssignmentChecker : AbstractFirPropertyInitializationChecker() { override fun analyze( graph: ControlFlowGraph, reporter: DiagnosticReporter, @@ -64,7 +64,7 @@ object VariableAssignmentChecker : AbstractFirCfaPropertyAssignmentChecker() { destructuringCanBeVal = false } lastDestructuredVariables-- - } else if (canBeVal(symbol, value) && symbol.fir.delegate == null) { + } else if (canBeVal(symbol, value) && symbol.fir.delegate == null ) { reporter.report(source, FirErrors.CAN_BE_VAL) } } From e2016499f301ce81db4a83a6742b80e5012f863a Mon Sep 17 00:00:00 2001 From: vldf Date: Mon, 17 Aug 2020 15:41:43 +0300 Subject: [PATCH 3/4] [FIR] Fixed CFA bug --- .../kotlin/fir/analysis/cfa/FirControlFlowAnalyzer.kt | 4 ++-- .../components/ControlFlowAnalysisDiagnosticComponent.kt | 5 ++++- .../diagnostics/tests/controlFlowAnalysis/kt2960.fir.kt | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) 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 cea30565043..3e891e96536 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 = AbstractFirPropertyInitializationChecker.LocalPropertyCollector.collect(graph) + val properties = AbstractFirCfaPropertyAssignmentChecker.LocalPropertyCollector.collect(graph) if (properties.isEmpty()) return - val data = AbstractFirPropertyInitializationChecker.DataCollector(properties).getData(graph) + val data = AbstractFirCfaPropertyAssignmentChecker.DataCollector(properties).getData(graph) variableAssignmentCheckers.forEach { it.analyze(graph, reporter, data, properties) } } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ControlFlowAnalysisDiagnosticComponent.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ControlFlowAnalysisDiagnosticComponent.kt index 49d8348a8ab..82c624c44d0 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ControlFlowAnalysisDiagnosticComponent.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ControlFlowAnalysisDiagnosticComponent.kt @@ -58,7 +58,10 @@ class ControlFlowAnalysisDiagnosticComponent(collector: AbstractDiagnosticCollec } override fun visitPropertyAccessor(propertyAccessor: FirPropertyAccessor, data: CheckerContext) { - visitFunction(propertyAccessor, data) + val graph = propertyAccessor.controlFlowGraphReference?.controlFlowGraph ?: return + runCheck { + controlFlowAnalyzer.analyzePropertyAccessor(propertyAccessor, graph, data, it) + } } override fun visitConstructor(constructor: FirConstructor, data: CheckerContext) { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt2960.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt2960.fir.kt index e6e2293f0df..24429bf8014 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt2960.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/kt2960.fir.kt @@ -15,7 +15,7 @@ val o = object { p.x = 4 val z : Int - doSmth(z) + doSmth(z) } } From 0d6b363179f62830625f4a6065e64b09c50423b4 Mon Sep 17 00:00:00 2001 From: vldf Date: Tue, 18 Aug 2020 11:33:27 +0300 Subject: [PATCH 4/4] [FIR] Refactoring for extended checkers --- ...bstractFirPropertyInitializationChecker.kt | 92 +--------------- .../kotlin/fir/analysis/cfa/CfaUtils.kt | 103 ++++++++++++++++++ .../analysis/cfa/FirControlFlowAnalyzer.kt | 4 +- .../FirSealedClassConstructorCallChecker.kt | 6 +- .../expression/FirAbstractSuperCallChecker.kt | 8 +- .../expression/FirExpressionChecker.kt | 2 +- .../expression/FirNotASupertypeChecker.kt | 6 +- ...rojectionsOnNonClassTypeArgumentChecker.kt | 4 +- ...upertypeExtendedByOtherSupertypeChecker.kt | 4 +- .../expression/FirSuperNotAvailableChecker.kt | 6 +- ...rclassNotAccessibleFromInterfaceChecker.kt | 6 +- ...ypeArgumentsNotAllowedExpressionChecker.kt | 4 +- .../FirUpperBoundViolatedChecker.kt | 10 +- .../ArrayEqualityCanBeReplacedWithEquals.kt | 14 +-- ...BeReplacedWithOperatorAssignmentChecker.kt | 18 ++- .../checkers/extended/EmptyRangeChecker.kt | 15 +-- .../RedundantCallOfConversionMethod.kt | 12 +- .../extended/VariableAssignmentChecker.kt | 1 + 18 files changed, 165 insertions(+), 150 deletions(-) create mode 100644 compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/CfaUtils.kt 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