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..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,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.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.* @@ -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..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,100 +5,16 @@ 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.resolve.dfa.cfg.CFGNode +import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol -abstract class AbstractFirPropertyInitializationChecker : FirControlFlowChecker() { - abstract override fun analyze(graph: ControlFlowGraph, reporter: DiagnosticReporter) - - 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) - } - } +abstract class AbstractFirPropertyInitializationChecker { + abstract fun analyze( + graph: ControlFlowGraph, + reporter: DiagnosticReporter, + data: Map, PropertyInitializationInfo>, + properties: Set + ) } \ 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 a8643df6d60..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 @@ -12,23 +12,44 @@ 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) { - 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 - 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 - checkers.forEach { it.analyze(graph, reporter) } + + cfaCheckers.forEach { it.analyze(graph, reporter) } + 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 = LocalPropertyCollector.collect(graph) + if (properties.isEmpty()) return + 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/cfa/FirPropertyInitializationAnalyzer.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/cfa/FirPropertyInitializationAnalyzer.kt index 66de5563e93..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 @@ -16,15 +16,23 @@ 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) + override fun analyze( + graph: ControlFlowGraph, + reporter: DiagnosticReporter, + data: Map, PropertyInitializationInfo>, + properties: Set + ) { + 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 2c75abb1621..5cbe1b9e130 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 @@ -29,7 +29,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.visibilityModifierType @@ -186,7 +185,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 @@ -248,20 +247,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 } } @@ -270,10 +269,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..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,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.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/DefaultDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DefaultDeclarationCheckers.kt index 78c4b3cffc7..372b8604950 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 @@ -52,8 +53,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 c23c1772aa3..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.checkers.cfa.FirControlFlowChecker +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 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/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 b41a69b2bfa..e73be836a31 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 @@ -15,15 +15,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 is FirClass<*> } 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 cff735555c6..525a1897d94 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 7fea895d4b2..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 @@ -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 +object CanBeReplacedWithOperatorAssignmentChecker : FirVariableAssignmentChecker() { + override fun check(expression: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) { + val lValue = expression.lValue if (lValue !is FirResolvedNamedReference) return - val operator = functionCall.psi?.children?.getOrNull(1) ?: return - if (operator.text != "=") 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 = functionCall.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 @@ -56,9 +59,10 @@ 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 + 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,9 +36,34 @@ object EmptyRangeChecker : FirBasicExpresionChecker() { } if (needReport) { - reporter.report(functionCall.source, FirErrors.EMPTY_RANGE) + reporter.report(expression.source, FirErrors.EMPTY_RANGE) } } + 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 + } + + // 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 + 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..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,18 +26,18 @@ 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) } } - 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..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 @@ -23,15 +24,16 @@ import org.jetbrains.kotlin.psi.KtProperty object VariableAssignmentChecker : AbstractFirPropertyInitializationChecker() { - override fun analyze(graph: ControlFlowGraph, reporter: DiagnosticReporter) { + 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) { 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) } }