[FIR] Checkers performance improvement and some refactoring

This commit is contained in:
vldf
2020-08-05 17:39:43 +03:00
parent 5a45cc7290
commit 638a0d3b82
15 changed files with 112 additions and 56 deletions
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis
import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSessionComponent 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.cfa.FirControlFlowChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.* import org.jetbrains.kotlin.fir.analysis.checkers.declaration.*
import org.jetbrains.kotlin.fir.analysis.checkers.expression.* import org.jetbrains.kotlin.fir.analysis.checkers.expression.*
@@ -70,6 +71,8 @@ private class ComposedDeclarationCheckers : DeclarationCheckers() {
get() = _constructorCheckers get() = _constructorCheckers
override val controlFlowAnalyserCheckers: List<FirControlFlowChecker> override val controlFlowAnalyserCheckers: List<FirControlFlowChecker>
get() = _controlFlowAnalyserCheckers get() = _controlFlowAnalyserCheckers
override val variableAssignmentCfaBasedCheckers: List<AbstractFirCfaPropertyAssignmentChecker>
get() = _variableAssignmentCfaBasedCheckers
private val _fileCheckers: MutableList<FirFileChecker> = mutableListOf() private val _fileCheckers: MutableList<FirFileChecker> = mutableListOf()
private val _declarationCheckers: MutableList<FirBasicDeclarationChecker> = mutableListOf() private val _declarationCheckers: MutableList<FirBasicDeclarationChecker> = mutableListOf()
@@ -77,6 +80,7 @@ private class ComposedDeclarationCheckers : DeclarationCheckers() {
private val _regularClassCheckers: MutableList<FirRegularClassChecker> = mutableListOf() private val _regularClassCheckers: MutableList<FirRegularClassChecker> = mutableListOf()
private val _constructorCheckers: MutableList<FirConstructorChecker> = mutableListOf() private val _constructorCheckers: MutableList<FirConstructorChecker> = mutableListOf()
private val _controlFlowAnalyserCheckers: MutableList<FirControlFlowChecker> = mutableListOf() private val _controlFlowAnalyserCheckers: MutableList<FirControlFlowChecker> = mutableListOf()
private val _variableAssignmentCfaBasedCheckers: MutableList<AbstractFirCfaPropertyAssignmentChecker> = mutableListOf()
fun register(checkers: DeclarationCheckers) { fun register(checkers: DeclarationCheckers) {
_fileCheckers += checkers.allFileCheckers _fileCheckers += checkers.allFileCheckers
@@ -85,6 +89,7 @@ private class ComposedDeclarationCheckers : DeclarationCheckers() {
_regularClassCheckers += checkers.allRegularClassCheckers _regularClassCheckers += checkers.allRegularClassCheckers
_constructorCheckers += checkers.allConstructorCheckers _constructorCheckers += checkers.allConstructorCheckers
_controlFlowAnalyserCheckers += checkers.controlFlowAnalyserCheckers _controlFlowAnalyserCheckers += checkers.controlFlowAnalyserCheckers
_variableAssignmentCfaBasedCheckers += checkers.variableAssignmentCfaBasedCheckers
} }
} }
@@ -8,14 +8,18 @@ package org.jetbrains.kotlin.fir.analysis.cfa
import kotlinx.collections.immutable.PersistentMap import kotlinx.collections.immutable.PersistentMap
import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.persistentMapOf
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange 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.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.* import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
abstract class AbstractFirPropertyInitializationChecker : FirControlFlowChecker() { abstract class AbstractFirCfaPropertyAssignmentChecker {
abstract override fun analyze(graph: ControlFlowGraph, reporter: DiagnosticReporter) abstract fun analyze(
graph: ControlFlowGraph,
reporter: DiagnosticReporter,
data: Map<CFGNode<*>, PropertyInitializationInfo>,
properties: Set<FirPropertySymbol>
)
class PropertyInitializationInfo( class PropertyInitializationInfo(
map: PersistentMap<FirPropertySymbol, EventOccurrencesRange> = persistentMapOf() map: PersistentMap<FirPropertySymbol, EventOccurrencesRange> = persistentMapOf()
@@ -15,7 +15,8 @@ import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
class FirControlFlowAnalyzer(session: FirSession) { 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) { fun analyzeClassInitializer(klass: FirClass<*>, graph: ControlFlowGraph, context: CheckerContext, reporter: DiagnosticReporter) {
if (graph.owner != null) return if (graph.owner != null) return
@@ -24,7 +25,14 @@ class FirControlFlowAnalyzer(session: FirSession) {
fun analyzeFunction(function: FirFunction<*>, graph: ControlFlowGraph, context: CheckerContext, reporter: DiagnosticReporter) { fun analyzeFunction(function: FirFunction<*>, graph: ControlFlowGraph, context: CheckerContext, reporter: DiagnosticReporter) {
if (graph.owner != null) return 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) { fun analyzePropertyInitializer(property: FirProperty, graph: ControlFlowGraph, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -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.ControlFlowGraphVisitorVoid
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.QualifiedAccessNode import org.jetbrains.kotlin.fir.resolve.dfa.cfg.QualifiedAccessNode
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
object FirPropertyInitializationAnalyzer : AbstractFirPropertyInitializationChecker() { object FirPropertyInitializationAnalyzer : AbstractFirCfaPropertyAssignmentChecker() {
override fun analyze(graph: ControlFlowGraph, reporter: DiagnosticReporter) { override fun analyze(
val localProperties = LocalPropertyCollector.collect(graph) graph: ControlFlowGraph,
// we want to analyze only properties without initializers reporter: DiagnosticReporter,
localProperties.retainAll { it.fir.initializer == null && it.fir.delegate == null } data: Map<CFGNode<*>, PropertyInitializationInfo>,
if (localProperties.isEmpty()) return properties: Set<FirPropertySymbol>
val data = DataCollector(localProperties).getData(graph) ) {
val reporterVisitor = UninitializedPropertyReporter(data, localProperties, reporter) // 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) graph.traverse(TraverseDirection.Forward, reporterVisitor)
} }
@@ -27,7 +27,6 @@ import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.FirTypeRef import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtModifierList import org.jetbrains.kotlin.psi.KtModifierList
import org.jetbrains.kotlin.psi.psiUtil.toVisibility 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. * or null if no such item could be found.
*/ */
fun CheckerContext.findClosestClassOrObject(): FirClass<*>? { fun CheckerContext.findClosestClassOrObject(): FirClass<*>? {
for (it in containingDeclarations.reversed()) { for (it in containingDeclarations.asReversed()) {
if ( if (
it is FirRegularClass || it is FirRegularClass ||
it is FirAnonymousObject it is FirAnonymousObject
@@ -237,20 +236,20 @@ fun FirClass<*>.modality(): Modality? {
/** /**
* returns implicit modality by FirMemberDeclaration * 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 is FirRegularClass && (this.classKind == ClassKind.CLASS || this.classKind == ClassKind.OBJECT)) {
if (this.classKind == ClassKind.INTERFACE) return KtTokens.ABSTRACT_KEYWORD if (this.classKind == ClassKind.INTERFACE) return Modality.ABSTRACT
return KtTokens.FINAL_KEYWORD return Modality.FINAL
} }
val klass = context.findClosestClassOrObject() ?: return KtTokens.FINAL_KEYWORD val klass = context.findClosestClassOrObject() ?: return Modality.FINAL
val modifiers = this.modifierListOrNull() ?: return KtTokens.FINAL_KEYWORD val modifiers = this.modifierListOrNull() ?: return Modality.FINAL
if (modifiers.hasModifier(KtTokens.OVERRIDE_KEYWORD)) { if (modifiers.hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
val klassModifiers = klass.modifierListOrNull() val klassModifiers = klass.modifierListOrNull()
if (klassModifiers != null && klassModifiers.run { if (klassModifiers != null && klassModifiers.run {
hasModifier(KtTokens.ABSTRACT_KEYWORD) || hasModifier(KtTokens.OPEN_KEYWORD) || hasModifier(KtTokens.SEALED_KEYWORD) 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 && klass.classKind == ClassKind.INTERFACE
&& !modifiers.hasModifier(KtTokens.PRIVATE_KEYWORD) && !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 private fun FirDeclaration.modifierListOrNull() = (this.source.getModifierList() as? FirPsiModifierList)?.modifierList
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.analysis.checkers.declaration 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 import org.jetbrains.kotlin.fir.analysis.checkers.cfa.FirControlFlowChecker
abstract class DeclarationCheckers { abstract class DeclarationCheckers {
@@ -18,6 +19,7 @@ abstract class DeclarationCheckers {
open val regularClassCheckers: List<FirRegularClassChecker> = emptyList() open val regularClassCheckers: List<FirRegularClassChecker> = emptyList()
open val constructorCheckers: List<FirConstructorChecker> = emptyList() open val constructorCheckers: List<FirConstructorChecker> = emptyList()
open val controlFlowAnalyserCheckers: List<FirControlFlowChecker> = emptyList() open val controlFlowAnalyserCheckers: List<FirControlFlowChecker> = emptyList()
open val variableAssignmentCfaBasedCheckers: List<AbstractFirCfaPropertyAssignmentChecker> = emptyList()
internal val allFileCheckers: List<FirFileChecker> get() = fileCheckers + declarationCheckers internal val allFileCheckers: List<FirFileChecker> get() = fileCheckers + declarationCheckers
internal val allMemberDeclarationCheckers: List<FirMemberDeclarationChecker> get() = memberDeclarationCheckers + declarationCheckers internal val allMemberDeclarationCheckers: List<FirMemberDeclarationChecker> get() = memberDeclarationCheckers + declarationCheckers
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.fir.analysis.checkers.declaration 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.* import org.jetbrains.kotlin.fir.analysis.checkers.extended.*
object ExtendedDeclarationCheckers : DeclarationCheckers() { object ExtendedDeclarationCheckers : DeclarationCheckers() {
@@ -20,7 +20,7 @@ object ExtendedDeclarationCheckers : DeclarationCheckers() {
RedundantSetterParameterTypeChecker RedundantSetterParameterTypeChecker
) )
override val controlFlowAnalyserCheckers: List<FirControlFlowChecker> = listOf( override val variableAssignmentCfaBasedCheckers: List<AbstractFirCfaPropertyAssignmentChecker> = listOf(
VariableAssignmentChecker VariableAssignmentChecker
) )
} }
@@ -22,16 +22,19 @@ import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.KtOperationReferenceExpression
object CanBeReplacedWithOperatorAssignmentChecker : FirExpressionChecker<FirVariableAssignment>() { object CanBeReplacedWithOperatorAssignmentChecker : FirExpressionChecker<FirVariableAssignment>() {
override fun check(functionCall: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(assignment: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) {
val lValue = functionCall.lValue val lValue = assignment.lValue
if (lValue !is FirResolvedNamedReference) return 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 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 rValuePsi = rValue.psi as? KtBinaryExpression ?: return
val rValueClassId = rValue.explicitReceiver?.typeRef?.coneType?.classId val rValueClassId = rValue.explicitReceiver?.typeRef?.coneType?.classId
@@ -56,9 +59,12 @@ object CanBeReplacedWithOperatorAssignmentChecker : FirExpressionChecker<FirVari
val isLeftMatch = isHierarchicallyTrue(operationToken, leftExpression?.operationToken) val isLeftMatch = isHierarchicallyTrue(operationToken, leftExpression?.operationToken)
&& leftExpression?.matcher(variable) == true && leftExpression?.matcher(variable) == true
if (isLeftMatch) return true
val isRightMatch = isHierarchicallyTrue(operationToken, rightExpression?.operationToken) val isRightMatch = isHierarchicallyTrue(operationToken, rightExpression?.operationToken)
&& rightExpression?.matcher(variable) == true && rightExpression?.matcher(variable) == true
isLeftMatch or isRightMatch if (isRightMatch) return true
false
} else { } else {
val leftExpression = left as? KtBinaryExpression val leftExpression = left as? KtBinaryExpression
@@ -10,17 +10,17 @@ import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpresionChecker import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpresionChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirStatement import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.psi import org.jetbrains.kotlin.fir.scopes.impl.FirIntegerOperatorCall
object EmptyRangeChecker : FirBasicExpresionChecker() { object EmptyRangeChecker : FirBasicExpresionChecker() {
override fun check(functionCall: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(functionCall: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
if (functionCall.source is FirFakeSourceElement<*>) return if (functionCall.source is FirFakeSourceElement<*>) return
if (functionCall !is FirFunctionCall) return if (functionCall !is FirFunctionCall) return
val range = functionCall.psi ?: return val left = functionCall.rangeLeft ?: return
val left = range.children.getOrNull(0)?.text?.toLongOrNull() ?: return val right = functionCall.rangeRight ?: return
val right = range.children.getOrNull(2)?.text?.toLongOrNull() ?: return
val needReport = when (functionCall.calleeReference.name.asString()) { val needReport = when (functionCall.calleeReference.name.asString()) {
"rangeTo" -> { "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
}
} }
@@ -37,7 +37,7 @@ object RedundantCallOfConversionMethod : FirQualifiedAccessChecker() {
} }
} }
private val targetClassMap = mapOf( private val targetClassMap = hashMapOf(
"toString" to StandardClassIds.String, "toString" to StandardClassIds.String,
"toDouble" to StandardClassIds.Double, "toDouble" to StandardClassIds.Double,
"toFloat" to StandardClassIds.Float, "toFloat" to StandardClassIds.Float,
@@ -5,8 +5,8 @@
package org.jetbrains.kotlin.fir.analysis.checkers.extended 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.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirFakeSourceElement import org.jetbrains.kotlin.fir.FirFakeSourceElement
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMemberDeclarationChecker 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.analysis.diagnostics.FirErrors.REDUNDANT_MODALITY_MODIFIER
import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration 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.psi
import org.jetbrains.kotlin.fir.toFirPsiSourceElement 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.KtDeclaration
import org.jetbrains.kotlin.psi.psiUtil.modalityModifier import org.jetbrains.kotlin.psi.psiUtil.modalityModifier
@@ -26,10 +25,9 @@ object RedundantModalityModifierChecker : FirMemberDeclarationChecker() {
override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration.source is FirFakeSourceElement<*>) return if (declaration.source is FirFakeSourceElement<*>) return
val modalityModifier = (declaration.psi as? KtDeclaration)?.modalityModifier() ?: return val modality = declaration.modality ?: return
val modality = (modalityModifier as? LeafPsiElement)?.elementType as? KtModifierKeywordToken ?: return
if ( if (
modality == KtTokens.FINAL_KEYWORD modality == Modality.FINAL
&& (context.containingDeclarations.last() as? FirClass<*>)?.classKind == ClassKind.INTERFACE && (context.containingDeclarations.last() as? FirClass<*>)?.classKind == ClassKind.INTERFACE
) return ) return
@@ -37,6 +35,7 @@ object RedundantModalityModifierChecker : FirMemberDeclarationChecker() {
if (modality != implicitModality) return if (modality != implicitModality) return
val modalityModifier = (declaration.psi as? KtDeclaration)?.modalityModifier() ?: return
reporter.report(modalityModifier.toFirPsiSourceElement(), REDUNDANT_MODALITY_MODIFIER) reporter.report(modalityModifier.toFirPsiSourceElement(), REDUNDANT_MODALITY_MODIFIER)
} }
} }
@@ -19,10 +19,9 @@ object RedundantReturnUnitType : FirBasicDeclarationChecker() {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration !is FirSimpleFunction) return if (declaration !is FirSimpleFunction) return
if (declaration.body is FirSingleExpressionBlock) return if (declaration.body is FirSingleExpressionBlock) return
if (declaration.returnTypeRef.source == null) return
if (declaration.source is FirFakeSourceElement<*>) return
val returnType = declaration.returnTypeRef val returnType = declaration.returnTypeRef
if (returnType.source == null) return
if (declaration.source is FirFakeSourceElement<*>) return
if (returnType.annotations.isNotEmpty()) return if (returnType.annotations.isNotEmpty()) return
if (returnType.isUnit) { if (returnType.isUnit) {
@@ -21,6 +21,7 @@ object RedundantSetterParameterTypeChecker : FirMemberDeclarationChecker() {
val valueParameter = setter.valueParameters.firstOrNull() ?: return val valueParameter = setter.valueParameters.firstOrNull() ?: return
val propertyTypeSource = declaration.returnTypeRef.source val propertyTypeSource = declaration.returnTypeRef.source
val setterParameterTypeSource = valueParameter.returnTypeRef.source ?: return val setterParameterTypeSource = valueParameter.returnTypeRef.source ?: return
if (setterParameterTypeSource != propertyTypeSource) { if (setterParameterTypeSource != propertyTypeSource) {
reporter.report(setterParameterTypeSource, REDUNDANT_SETTER_PARAMETER_TYPE) reporter.report(setterParameterTypeSource, REDUNDANT_SETTER_PARAMETER_TYPE)
} }
@@ -33,7 +33,6 @@ object RedundantSingleExpressionStringTemplateChecker : FirBasicExpresionChecker
private fun PsiElement.findStringParent(): KtStringTemplateExpression? { private fun PsiElement.findStringParent(): KtStringTemplateExpression? {
if (this is KtStringTemplateExpression) return this if (this is KtStringTemplateExpression) return this
return if (this.parent != null) this.parent.findStringParent() return this.parent?.findStringParent()
else null
} }
} }
@@ -9,7 +9,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers.extended
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.fir.FirFakeSourceElement import org.jetbrains.kotlin.fir.FirFakeSourceElement
import org.jetbrains.kotlin.fir.FirSourceElement 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.TraverseDirection
import org.jetbrains.kotlin.fir.analysis.cfa.traverse import org.jetbrains.kotlin.fir.analysis.cfa.traverse
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
@@ -22,16 +22,17 @@ import org.jetbrains.kotlin.fir.toFirPsiSourceElement
import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtProperty
object VariableAssignmentChecker : AbstractFirPropertyInitializationChecker() { object VariableAssignmentChecker : AbstractFirCfaPropertyAssignmentChecker() {
override fun analyze(graph: ControlFlowGraph, reporter: DiagnosticReporter) { override fun analyze(
graph: ControlFlowGraph,
reporter: DiagnosticReporter,
data: Map<CFGNode<*>, PropertyInitializationInfo>,
properties: Set<FirPropertySymbol>
) {
val unprocessedProperties = mutableSetOf<FirPropertySymbol>() val unprocessedProperties = mutableSetOf<FirPropertySymbol>()
val propertiesCharacteristics = mutableMapOf<FirPropertySymbol, EventOccurrencesRange>() val propertiesCharacteristics = mutableMapOf<FirPropertySymbol, EventOccurrencesRange>()
val localProperties = LocalPropertyCollector.collect(graph) val reporterVisitor = UninitializedPropertyReporter(data, properties, unprocessedProperties, propertiesCharacteristics)
if (localProperties.isEmpty()) return
val data = DataCollector(localProperties).getData(graph)
val reporterVisitor = UninitializedPropertyReporter(data, localProperties, unprocessedProperties, propertiesCharacteristics)
graph.traverse(TraverseDirection.Forward, reporterVisitor) graph.traverse(TraverseDirection.Forward, reporterVisitor)
for (property in unprocessedProperties) { for (property in unprocessedProperties) {
@@ -63,7 +64,7 @@ object VariableAssignmentChecker : AbstractFirPropertyInitializationChecker() {
destructuringCanBeVal = false destructuringCanBeVal = false
} }
lastDestructuredVariables-- 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) reporter.report(source, FirErrors.CAN_BE_VAL)
} }
} }