[FIR] Add CanBeReplacedWithOperatorAssignmentChecker

This commit is contained in:
vldf
2020-07-22 10:31:34 +03:00
committed by Mikhail Glukhikh
parent fa8c6e7fb6
commit eadd3f00f2
46 changed files with 424 additions and 8 deletions
@@ -0,0 +1,7 @@
fun goo() {
var a = 2
val b = 4
a <!CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT!>=<!> a + 1 + b
a <!CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT!>=<!> (a + 1)
a = a * b + 1
}
@@ -0,0 +1,8 @@
FILE: BasicTest.kt
public final fun goo(): R|kotlin/Unit| {
lvar a: R|kotlin/Int| = Int(2)
lval b: R|kotlin/Int| = Int(4)
R|<local>/a| = R|<local>/a|.R|kotlin/Int.plus|(Int(1)).R|kotlin/Int.plus|(R|<local>/b|)
R|<local>/a| = R|<local>/a|.R|kotlin/Int.plus|(Int(1))
R|<local>/a| = R|<local>/a|.R|kotlin/Int.times|(R|<local>/b|).R|kotlin/Int.plus|(Int(1))
}
@@ -0,0 +1,4 @@
fun foo() {
var a = 0
a = (a + 1) / 2
}
@@ -0,0 +1,5 @@
FILE: ComplexExpression.kt
public final fun foo(): R|kotlin/Unit| {
lvar a: R|kotlin/Int| = Int(0)
R|<local>/a| = R|<local>/a|.R|kotlin/Int.plus|(Int(1)).R|kotlin/Int.div|(Int(2))
}
@@ -0,0 +1,4 @@
fun foo() {
var a = 0
a += 10 + a
}
@@ -0,0 +1,5 @@
FILE: OperatorAssignment.kt
public final fun foo(): R|kotlin/Unit| {
lvar a: R|kotlin/Int| = Int(0)
R|<local>/a| = R|<local>/a|.R|kotlin/Int.plus|(Int(10).R|kotlin/Int.plus|(R|<local>/a|))
}
@@ -0,0 +1,6 @@
// WITH_RUNTIME
fun foo() {
var list1 = java.util.Collections.emptyList<String>()
val list2 = listOf("b")
list1 = list1 + list2
}
@@ -0,0 +1,6 @@
FILE: flexibleTypeBug.kt
public final fun foo(): R|kotlin/Unit| {
lvar list1: R|ft<kotlin/collections/MutableList<ft<kotlin/String, kotlin/String?>!>, kotlin/collections/List<ft<kotlin/String, kotlin/String?>!>?>!| = Q|java/util/Collections|.R|java/util/Collections.emptyList|<R|ft<kotlin/String, kotlin/String?>!|>()
lval list2: R|kotlin/collections/List<kotlin/String>| = R|kotlin/collections/listOf|<R|kotlin/String|>(String(b))
R|<local>/list1| = R|<local>/list1|.R|kotlin/collections/plus|<R|ft<kotlin/String, kotlin/String?>!|>(R|<local>/list2|)
}
@@ -0,0 +1,4 @@
fun foo() {
var x = 0
x = x / 1 + 1
}
@@ -0,0 +1,5 @@
FILE: illegalMultipleOperators.kt
public final fun foo(): R|kotlin/Unit| {
lvar x: R|kotlin/Int| = Int(0)
R|<local>/x| = R|<local>/x|.R|kotlin/Int.div|(Int(1)).R|kotlin/Int.plus|(Int(1))
}
@@ -0,0 +1,5 @@
fun foo() {
var x = 0
val y = 0
x = y / x + 0
}
@@ -0,0 +1,6 @@
FILE: illegalMultipleOperatorsMiddle.kt
public final fun foo(): R|kotlin/Unit| {
lvar x: R|kotlin/Int| = Int(0)
lval y: R|kotlin/Int| = Int(0)
R|<local>/x| = R|<local>/y|.R|kotlin/Int.div|(R|<local>/x|).R|kotlin/Int.plus|(Int(0))
}
@@ -0,0 +1,4 @@
fun foo() {
var x = 0
x = 1 - x
}
@@ -0,0 +1,5 @@
FILE: invalidSubtraction.kt
public final fun foo(): R|kotlin/Unit| {
lvar x: R|kotlin/Int| = Int(0)
R|<local>/x| = Int(1).R|kotlin/Int.minus|(R|<local>/x|)
}
@@ -0,0 +1,7 @@
// WITH_RUNTIME
fun foo() {
var list = listOf(1, 2, 3)
// Should not be highlighted because it's the way we use to say explicitly
// "yes, we want to re-assign this immutable list"
list = list + 4
}
@@ -0,0 +1,5 @@
FILE: list.kt
public final fun foo(): R|kotlin/Unit| {
lvar list: R|kotlin/collections/List<kotlin/Int>| = R|kotlin/collections/listOf|<R|kotlin/Int|>(vararg(Int(1), Int(2), Int(3)))
R|<local>/list| = R|<local>/list|.R|kotlin/collections/plus|<R|kotlin/Int|>(Int(4))
}
@@ -0,0 +1,5 @@
fun foo() {
var x = 0
var y = 0
x <!CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT!>=<!> x + y + 5
}
@@ -0,0 +1,6 @@
FILE: multipleOperators.kt
public final fun foo(): R|kotlin/Unit| {
lvar x: R|kotlin/Int| = Int(0)
lvar y: R|kotlin/Int| = Int(0)
R|<local>/x| = R|<local>/x|.R|kotlin/Int.plus|(R|<local>/y|).R|kotlin/Int.plus|(Int(5))
}
@@ -0,0 +1,5 @@
fun foo() {
var x = 0
var y = 0
x <!CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT!>=<!> y + x + 5
}
@@ -0,0 +1,6 @@
FILE: multipleOperatorsRightSideRepeat.kt
public final fun foo(): R|kotlin/Unit| {
lvar x: R|kotlin/Int| = Int(0)
lvar y: R|kotlin/Int| = Int(0)
R|<local>/x| = R|<local>/y|.R|kotlin/Int.plus|(R|<local>/x|).R|kotlin/Int.plus|(Int(5))
}
@@ -0,0 +1,9 @@
// WITH_RUNTIME
fun foo() {
var listVar = mutableListOf(1, 2, 3)
// now, Idea hightlights this code like error (cuz listVar
// is mutable and listVar + 4 is immutable) and like warning
// (cuz can be replaced with +=)
listVar = listVar + 4
}
@@ -0,0 +1,5 @@
FILE: mutableList.kt
public final fun foo(): R|kotlin/Unit| {
lvar listVar: R|kotlin/collections/MutableList<kotlin/Int>| = R|kotlin/collections/mutableListOf|<R|kotlin/Int|>(vararg(Int(1), Int(2), Int(3)))
R|<local>/listVar| = R|<local>/listVar|.R|kotlin/collections/plus|<R|kotlin/Int|>(Int(4))
}
@@ -0,0 +1,8 @@
fun foo() {
var x = 0
x <!CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT!>=<!> x - 1 - 1
x <!CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT!>=<!> x / 1
x = 1 / x
x <!CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT!>=<!> -1 + x
}
@@ -0,0 +1,8 @@
FILE: nonCommutativeRepeat.kt
public final fun foo(): R|kotlin/Unit| {
lvar x: R|kotlin/Int| = Int(0)
R|<local>/x| = R|<local>/x|.R|kotlin/Int.minus|(Int(1)).R|kotlin/Int.minus|(Int(1))
R|<local>/x| = R|<local>/x|.R|kotlin/Int.div|(Int(1))
R|<local>/x| = Int(1).R|kotlin/Int.div|(R|<local>/x|)
R|<local>/x| = Int(1).R|kotlin/Int.unaryMinus|().R|kotlin/Int.plus|(R|<local>/x|)
}
@@ -0,0 +1,6 @@
fun foo() {
var x = 0
val y = 0
val z = 0
x = y + z
}
@@ -0,0 +1,7 @@
FILE: nonRepeatingAssignment.kt
public final fun foo(): R|kotlin/Unit| {
lvar x: R|kotlin/Int| = Int(0)
lval y: R|kotlin/Int| = Int(0)
lval z: R|kotlin/Int| = Int(0)
R|<local>/x| = R|<local>/y|.R|kotlin/Int.plus|(R|<local>/z|)
}
@@ -0,0 +1,10 @@
class A
operator fun A.plus(a: A): A = A()
operator fun A.plusAssign(a: A){}
fun foo() {
var a1 = A()
val a2 = A()
a1 = a1 + a2
}
@@ -0,0 +1,17 @@
FILE: plusAssignConflict.kt
public final class A : R|kotlin/Any| {
public constructor(): R|A| {
super<R|kotlin/Any|>()
}
}
public final operator fun R|A|.plus(a: R|A|): R|A| {
^plus R|/A.A|()
}
public final operator fun R|A|.plusAssign(a: R|A|): R|kotlin/Unit| {
}
public final fun foo(): R|kotlin/Unit| {
lvar a1: R|A| = R|/A.A|()
lval a2: R|A| = R|/A.A|()
R|<local>/a1| = R|<local>/a1|.R|/plus|(R|<local>/a2|)
}
@@ -0,0 +1,4 @@
fun foo() {
var x = 0
x <!CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT!>=<!> 1 + x
}
@@ -0,0 +1,5 @@
FILE: rightSideRepeat.kt
public final fun foo(): R|kotlin/Unit| {
lvar x: R|kotlin/Int| = Int(0)
R|<local>/x| = Int(1).R|kotlin/Int.plus|(R|<local>/x|)
}
@@ -0,0 +1,5 @@
fun foo() {
var y = 0
val x = 0
y <!CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT!>=<!> y + x
}
@@ -0,0 +1,6 @@
FILE: simpleAssign.kt
public final fun foo(): R|kotlin/Unit| {
lvar y: R|kotlin/Int| = Int(0)
lval x: R|kotlin/Int| = Int(0)
R|<local>/y| = R|<local>/y|.R|kotlin/Int.plus|(R|<local>/x|)
}
@@ -0,0 +1,4 @@
fun foo() {
var x = 0
x <!CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT!>=<!> x + 1
}
@@ -0,0 +1,5 @@
FILE: validAddition.kt
public final fun foo(): R|kotlin/Unit| {
lvar x: R|kotlin/Int| = Int(0)
R|<local>/x| = R|<local>/x|.R|kotlin/Int.plus|(Int(1))
}
@@ -0,0 +1,4 @@
fun foo() {
var x = 0
x <!CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT!>=<!> x - 1
}
@@ -0,0 +1,5 @@
FILE: validSubtraction.kt
public final fun foo(): R|kotlin/Unit| {
lvar x: R|kotlin/Int| = Int(0)
R|<local>/x| = R|<local>/x|.R|kotlin/Int.minus|(Int(1))
}
@@ -47,4 +47,107 @@ public class ExtendedFirDiagnosticsTestGenerated extends AbstractExtendedFirDiag
public void testRedundantVisibilityModifierChecker() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/RedundantVisibilityModifierChecker.kt");
}
@TestMetadata("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class CanBeReplacedWithOperatorAssignment extends AbstractExtendedFirDiagnosticsTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInCanBeReplacedWithOperatorAssignment() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("BasicTest.kt")
public void testBasicTest() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/BasicTest.kt");
}
@TestMetadata("ComplexExpression.kt")
public void testComplexExpression() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/ComplexExpression.kt");
}
@TestMetadata("flexibleTypeBug.kt")
public void testFlexibleTypeBug() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/flexibleTypeBug.kt");
}
@TestMetadata("illegalMultipleOperators.kt")
public void testIllegalMultipleOperators() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperators.kt");
}
@TestMetadata("illegalMultipleOperatorsMiddle.kt")
public void testIllegalMultipleOperatorsMiddle() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperatorsMiddle.kt");
}
@TestMetadata("invalidSubtraction.kt")
public void testInvalidSubtraction() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/invalidSubtraction.kt");
}
@TestMetadata("list.kt")
public void testList() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/list.kt");
}
@TestMetadata("multipleOperators.kt")
public void testMultipleOperators() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperators.kt");
}
@TestMetadata("multipleOperatorsRightSideRepeat.kt")
public void testMultipleOperatorsRightSideRepeat() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperatorsRightSideRepeat.kt");
}
@TestMetadata("mutableList.kt")
public void testMutableList() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/mutableList.kt");
}
@TestMetadata("nonCommutativeRepeat.kt")
public void testNonCommutativeRepeat() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonCommutativeRepeat.kt");
}
@TestMetadata("nonRepeatingAssignment.kt")
public void testNonRepeatingAssignment() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonRepeatingAssignment.kt");
}
@TestMetadata("OperatorAssignment.kt")
public void testOperatorAssignment() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/OperatorAssignment.kt");
}
@TestMetadata("plusAssignConflict.kt")
public void testPlusAssignConflict() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/plusAssignConflict.kt");
}
@TestMetadata("rightSideRepeat.kt")
public void testRightSideRepeat() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/rightSideRepeat.kt");
}
@TestMetadata("simpleAssign.kt")
public void testSimpleAssign() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/simpleAssign.kt");
}
@TestMetadata("validAddition.kt")
public void testValidAddition() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validAddition.kt");
}
@TestMetadata("validSubtraction.kt")
public void testValidSubtraction() throws Exception {
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validSubtraction.kt");
}
}
}
@@ -79,14 +79,18 @@ private class ComposedExpressionCheckers : ExpressionCheckers() {
get() = _qualifiedAccessCheckers
override val functionCallCheckers: List<FirFunctionCallChecker>
get() = _functionCallCheckers
override val variableAssignmentCheckers: List<FirVariableAssignmentChecker>
get() = _variableAssignmentCheckers
private val _expressionCheckers: MutableList<FirBasicExpresionChecker> = mutableListOf()
private val _qualifiedAccessCheckers: MutableList<FirQualifiedAccessChecker> = mutableListOf()
private val _functionCallCheckers: MutableList<FirFunctionCallChecker> = mutableListOf()
private val _variableAssignmentCheckers: MutableList<FirVariableAssignmentChecker> = mutableListOf()
fun register(checkers: ExpressionCheckers) {
_expressionCheckers += checkers.allExpressionCheckers
_qualifiedAccessCheckers += checkers.allQualifiedAccessCheckers
_functionCallCheckers += checkers.allFunctionCallCheckers
_variableAssignmentCheckers += checkers.variableAssignmentCheckers
}
}
@@ -5,10 +5,7 @@
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantExplicitTypeChecker
import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantModalityModifierChecker
import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantReturnUnitType
import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantVisibilityModifierChecker
import org.jetbrains.kotlin.fir.analysis.checkers.extended.*
object ExtendedDeclarationCheckers : DeclarationCheckers() {
override val declarationCheckers = listOf(
@@ -13,6 +13,7 @@ abstract class ExpressionCheckers {
open val expressionCheckers: List<FirBasicExpresionChecker> = emptyList()
open val qualifiedAccessCheckers: List<FirQualifiedAccessChecker> = emptyList()
open val functionCallCheckers: List<FirFunctionCallChecker> = emptyList()
open val variableAssignmentCheckers: List<FirVariableAssignmentChecker> = emptyList()
internal val allExpressionCheckers get() = expressionCheckers
internal val allQualifiedAccessCheckers get() = qualifiedAccessCheckers + allExpressionCheckers
@@ -0,0 +1,14 @@
/*
* 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.checkers.expression
import org.jetbrains.kotlin.fir.analysis.checkers.extended.CanBeReplacedWithOperatorAssignmentChecker
object ExtendedExpressionCheckers : ExpressionCheckers() {
override val variableAssignmentCheckers: List<FirVariableAssignmentChecker> = listOf(
CanBeReplacedWithOperatorAssignmentChecker
)
}
@@ -7,14 +7,16 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
abstract class FirExpressionChecker<in E : FirExpression> {
abstract class FirExpressionChecker<in E : FirStatement> {
abstract fun check(functionCall: E, context: CheckerContext, reporter: DiagnosticReporter)
}
typealias FirBasicExpresionChecker = FirExpressionChecker<FirExpression>
typealias FirBasicExpresionChecker = FirExpressionChecker<FirStatement>
typealias FirQualifiedAccessChecker = FirExpressionChecker<FirQualifiedAccessExpression>
typealias FirFunctionCallChecker = FirExpressionChecker<FirFunctionCall>
typealias FirVariableAssignmentChecker = FirExpressionChecker<FirVariableAssignment>
@@ -0,0 +1,72 @@
/*
* 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.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.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.toFirPsiSourceElement
import org.jetbrains.kotlin.fir.types.classId
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
object CanBeReplacedWithOperatorAssignmentChecker : FirExpressionChecker<FirVariableAssignment>() {
override fun check(functionCall: FirVariableAssignment, context: CheckerContext, reporter: DiagnosticReporter) {
val lValue = functionCall.lValue
if (lValue !is FirResolvedNamedReference) return
val operator = functionCall.psi?.children?.getOrNull(1) ?: return
if (operator.text != "=") return
val lValuePsi = lValue.psi as? KtNameReferenceExpression ?: return
val rValue = functionCall.rValue as? FirFunctionCall ?: return
val rValuePsi = rValue.psi as? KtBinaryExpression ?: return
val rValueClassId = rValue.explicitReceiver?.typeRef?.coneType?.classId
if (rValueClassId !in StandardClassIds.primitiveTypes) return
val rValueResolvedSymbol = rValue.toResolvedCallableSymbol() ?: return
if (rValueResolvedSymbol.callableId.classId !in StandardClassIds.primitiveTypes) return
if (rValuePsi.matcher(lValuePsi)) {
val operatorStatement = operator.toFirPsiSourceElement()
reporter.report(operatorStatement, FirErrors.CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT)
}
}
fun KtBinaryExpression.matcher(variable: KtNameReferenceExpression): Boolean {
if ((left as? KtNameReferenceExpression)?.getReferencedName() == variable.getReferencedName()) return true
if ((right as? KtNameReferenceExpression)?.getReferencedName() == variable.getReferencedName() && isCommutative()) return true
return if (isCommutative()) {
val leftExpression = left as? KtBinaryExpression
val rightExpression = right as? KtBinaryExpression
val isLeftMatch = isHierarchicallyTrue(operationToken, leftExpression?.operationToken)
&& leftExpression?.matcher(variable) == true
val isRightMatch = isHierarchicallyTrue(operationToken, rightExpression?.operationToken)
&& rightExpression?.matcher(variable) == true
isLeftMatch or isRightMatch
} else {
val leftExpression = left as? KtBinaryExpression
isHierarchicallyTrue(operationToken, leftExpression?.operationToken)
&& leftExpression?.matcher(variable) == true
}
}
private fun KtBinaryExpression.isCommutative() = this.operationToken == KtTokens.PLUS || this.operationToken == KtTokens.MUL
private fun isHierarchicallyTrue(currentOperation: IElementType, nextOperation: IElementType?) = currentOperation == nextOperation
}
@@ -79,7 +79,11 @@ class ExpressionCheckersDiagnosticComponent(collector: AbstractDiagnosticCollect
runCheck { checkers.expressionCheckers.check(getClassCall, data, it) }
}
private fun <E : FirExpression> List<FirExpressionChecker<E>>.check(expression: E, context: CheckerContext, reporter: DiagnosticReporter) {
override fun visitVariableAssignment(variableAssignment: FirVariableAssignment, data: CheckerContext) {
runCheck { checkers.variableAssignmentCheckers.check(variableAssignment, data, it) }
}
private fun <E : FirStatement> List<FirExpressionChecker<E>>.check(expression: E, context: CheckerContext, reporter: DiagnosticReporter) {
for (checker in this) {
checker.check(expression, context, reporter)
}
@@ -87,6 +87,7 @@ object FirErrors {
val REDUNDANT_MODALITY_MODIFIER by warning0<FirSourceElement, PsiElement>()
val REDUNDANT_RETURN_UNIT_TYPE by warning0<FirSourceElement, PsiTypeElement>()
val REDUNDANT_EXPLICIT_TYPE by warning0<FirSourceElement, PsiElement>()
val CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT by warning0<FirSourceElement, PsiElement>()
}
@@ -6,10 +6,12 @@
package org.jetbrains.kotlin.fir
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.ExtendedDeclarationCheckers
import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExtendedExpressionCheckers
import org.jetbrains.kotlin.fir.analysis.checkersComponent
abstract class AbstractExtendedFirDiagnosticsTest : AbstractFirDiagnosticsTest() {
override fun configureSession(session: FirSession) {
session.checkersComponent.register(ExtendedDeclarationCheckers)
session.checkersComponent.register(ExtendedExpressionCheckers)
}
}