diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/BasicTest.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/BasicTest.kt new file mode 100644 index 00000000000..8358b6595c7 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/BasicTest.kt @@ -0,0 +1,7 @@ +fun goo() { + var a = 2 + val b = 4 + a = a + 1 + b + a = (a + 1) + a = a * b + 1 +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/BasicTest.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/BasicTest.txt new file mode 100644 index 00000000000..9d6e84f9fb6 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/BasicTest.txt @@ -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|/a| = R|/a|.R|kotlin/Int.plus|(Int(1)).R|kotlin/Int.plus|(R|/b|) + R|/a| = R|/a|.R|kotlin/Int.plus|(Int(1)) + R|/a| = R|/a|.R|kotlin/Int.times|(R|/b|).R|kotlin/Int.plus|(Int(1)) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/ComplexExpression.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/ComplexExpression.kt new file mode 100644 index 00000000000..6754f127ad4 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/ComplexExpression.kt @@ -0,0 +1,4 @@ +fun foo() { + var a = 0 + a = (a + 1) / 2 +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/ComplexExpression.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/ComplexExpression.txt new file mode 100644 index 00000000000..6cab3755103 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/ComplexExpression.txt @@ -0,0 +1,5 @@ +FILE: ComplexExpression.kt + public final fun foo(): R|kotlin/Unit| { + lvar a: R|kotlin/Int| = Int(0) + R|/a| = R|/a|.R|kotlin/Int.plus|(Int(1)).R|kotlin/Int.div|(Int(2)) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/OperatorAssignment.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/OperatorAssignment.kt new file mode 100644 index 00000000000..97ff8586aea --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/OperatorAssignment.kt @@ -0,0 +1,4 @@ +fun foo() { + var a = 0 + a += 10 + a +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/OperatorAssignment.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/OperatorAssignment.txt new file mode 100644 index 00000000000..8d8ee31f60a --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/OperatorAssignment.txt @@ -0,0 +1,5 @@ +FILE: OperatorAssignment.kt + public final fun foo(): R|kotlin/Unit| { + lvar a: R|kotlin/Int| = Int(0) + R|/a| = R|/a|.R|kotlin/Int.plus|(Int(10).R|kotlin/Int.plus|(R|/a|)) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/flexibleTypeBug.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/flexibleTypeBug.kt new file mode 100644 index 00000000000..15464876e2a --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/flexibleTypeBug.kt @@ -0,0 +1,6 @@ +// WITH_RUNTIME +fun foo() { + var list1 = java.util.Collections.emptyList() + val list2 = listOf("b") + list1 = list1 + list2 +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/flexibleTypeBug.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/flexibleTypeBug.txt new file mode 100644 index 00000000000..9ad108acef4 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/flexibleTypeBug.txt @@ -0,0 +1,6 @@ +FILE: flexibleTypeBug.kt + public final fun foo(): R|kotlin/Unit| { + lvar list1: R|ft!>, kotlin/collections/List!>?>!| = Q|java/util/Collections|.R|java/util/Collections.emptyList|!|>() + lval list2: R|kotlin/collections/List| = R|kotlin/collections/listOf|(String(b)) + R|/list1| = R|/list1|.R|kotlin/collections/plus|!|>(R|/list2|) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperators.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperators.kt new file mode 100644 index 00000000000..bd15008d601 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperators.kt @@ -0,0 +1,4 @@ +fun foo() { + var x = 0 + x = x / 1 + 1 +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperators.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperators.txt new file mode 100644 index 00000000000..a7df481e988 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperators.txt @@ -0,0 +1,5 @@ +FILE: illegalMultipleOperators.kt + public final fun foo(): R|kotlin/Unit| { + lvar x: R|kotlin/Int| = Int(0) + R|/x| = R|/x|.R|kotlin/Int.div|(Int(1)).R|kotlin/Int.plus|(Int(1)) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperatorsMiddle.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperatorsMiddle.kt new file mode 100644 index 00000000000..25d3ab44a32 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperatorsMiddle.kt @@ -0,0 +1,5 @@ +fun foo() { + var x = 0 + val y = 0 + x = y / x + 0 +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperatorsMiddle.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperatorsMiddle.txt new file mode 100644 index 00000000000..bdc3263fed1 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/illegalMultipleOperatorsMiddle.txt @@ -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|/x| = R|/y|.R|kotlin/Int.div|(R|/x|).R|kotlin/Int.plus|(Int(0)) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/invalidSubtraction.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/invalidSubtraction.kt new file mode 100644 index 00000000000..f256d51086b --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/invalidSubtraction.kt @@ -0,0 +1,4 @@ +fun foo() { + var x = 0 + x = 1 - x +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/invalidSubtraction.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/invalidSubtraction.txt new file mode 100644 index 00000000000..9160cb425c0 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/invalidSubtraction.txt @@ -0,0 +1,5 @@ +FILE: invalidSubtraction.kt + public final fun foo(): R|kotlin/Unit| { + lvar x: R|kotlin/Int| = Int(0) + R|/x| = Int(1).R|kotlin/Int.minus|(R|/x|) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/list.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/list.kt new file mode 100644 index 00000000000..0c668efce9e --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/list.kt @@ -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 +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/list.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/list.txt new file mode 100644 index 00000000000..40e46275d73 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/list.txt @@ -0,0 +1,5 @@ +FILE: list.kt + public final fun foo(): R|kotlin/Unit| { + lvar list: R|kotlin/collections/List| = R|kotlin/collections/listOf|(vararg(Int(1), Int(2), Int(3))) + R|/list| = R|/list|.R|kotlin/collections/plus|(Int(4)) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperators.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperators.kt new file mode 100644 index 00000000000..ac476e5bb4d --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperators.kt @@ -0,0 +1,5 @@ +fun foo() { + var x = 0 + var y = 0 + x = x + y + 5 +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperators.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperators.txt new file mode 100644 index 00000000000..97875ed190d --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperators.txt @@ -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|/x| = R|/x|.R|kotlin/Int.plus|(R|/y|).R|kotlin/Int.plus|(Int(5)) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperatorsRightSideRepeat.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperatorsRightSideRepeat.kt new file mode 100644 index 00000000000..bc17c7ba1b5 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperatorsRightSideRepeat.kt @@ -0,0 +1,5 @@ +fun foo() { + var x = 0 + var y = 0 + x = y + x + 5 +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperatorsRightSideRepeat.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperatorsRightSideRepeat.txt new file mode 100644 index 00000000000..af69c51b2b1 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/multipleOperatorsRightSideRepeat.txt @@ -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|/x| = R|/y|.R|kotlin/Int.plus|(R|/x|).R|kotlin/Int.plus|(Int(5)) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/mutableList.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/mutableList.kt new file mode 100644 index 00000000000..fcf8a7bcc40 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/mutableList.kt @@ -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 +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/mutableList.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/mutableList.txt new file mode 100644 index 00000000000..5c250b98f39 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/mutableList.txt @@ -0,0 +1,5 @@ +FILE: mutableList.kt + public final fun foo(): R|kotlin/Unit| { + lvar listVar: R|kotlin/collections/MutableList| = R|kotlin/collections/mutableListOf|(vararg(Int(1), Int(2), Int(3))) + R|/listVar| = R|/listVar|.R|kotlin/collections/plus|(Int(4)) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonCommutativeRepeat.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonCommutativeRepeat.kt new file mode 100644 index 00000000000..42a73c4c6c5 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonCommutativeRepeat.kt @@ -0,0 +1,8 @@ +fun foo() { + var x = 0 + x = x - 1 - 1 + + x = x / 1 + x = 1 / x + x = -1 + x +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonCommutativeRepeat.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonCommutativeRepeat.txt new file mode 100644 index 00000000000..a46a3cfb16a --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonCommutativeRepeat.txt @@ -0,0 +1,8 @@ +FILE: nonCommutativeRepeat.kt + public final fun foo(): R|kotlin/Unit| { + lvar x: R|kotlin/Int| = Int(0) + R|/x| = R|/x|.R|kotlin/Int.minus|(Int(1)).R|kotlin/Int.minus|(Int(1)) + R|/x| = R|/x|.R|kotlin/Int.div|(Int(1)) + R|/x| = Int(1).R|kotlin/Int.div|(R|/x|) + R|/x| = Int(1).R|kotlin/Int.unaryMinus|().R|kotlin/Int.plus|(R|/x|) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonRepeatingAssignment.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonRepeatingAssignment.kt new file mode 100644 index 00000000000..5ba73f3f09a --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonRepeatingAssignment.kt @@ -0,0 +1,6 @@ +fun foo() { + var x = 0 + val y = 0 + val z = 0 + x = y + z +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonRepeatingAssignment.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonRepeatingAssignment.txt new file mode 100644 index 00000000000..f639e88ae5f --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/nonRepeatingAssignment.txt @@ -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|/x| = R|/y|.R|kotlin/Int.plus|(R|/z|) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/plusAssignConflict.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/plusAssignConflict.kt new file mode 100644 index 00000000000..a297b550562 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/plusAssignConflict.kt @@ -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 +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/plusAssignConflict.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/plusAssignConflict.txt new file mode 100644 index 00000000000..e1999643070 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/plusAssignConflict.txt @@ -0,0 +1,17 @@ +FILE: plusAssignConflict.kt + public final class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + } + 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|/a1| = R|/a1|.R|/plus|(R|/a2|) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/rightSideRepeat.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/rightSideRepeat.kt new file mode 100644 index 00000000000..d6812063422 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/rightSideRepeat.kt @@ -0,0 +1,4 @@ +fun foo() { + var x = 0 + x = 1 + x +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/rightSideRepeat.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/rightSideRepeat.txt new file mode 100644 index 00000000000..05fa360f063 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/rightSideRepeat.txt @@ -0,0 +1,5 @@ +FILE: rightSideRepeat.kt + public final fun foo(): R|kotlin/Unit| { + lvar x: R|kotlin/Int| = Int(0) + R|/x| = Int(1).R|kotlin/Int.plus|(R|/x|) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/simpleAssign.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/simpleAssign.kt new file mode 100644 index 00000000000..8e4a5426069 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/simpleAssign.kt @@ -0,0 +1,5 @@ +fun foo() { + var y = 0 + val x = 0 + y = y + x +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/simpleAssign.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/simpleAssign.txt new file mode 100644 index 00000000000..55b42e14443 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/simpleAssign.txt @@ -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|/y| = R|/y|.R|kotlin/Int.plus|(R|/x|) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validAddition.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validAddition.kt new file mode 100644 index 00000000000..38873894637 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validAddition.kt @@ -0,0 +1,4 @@ +fun foo() { + var x = 0 + x = x + 1 +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validAddition.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validAddition.txt new file mode 100644 index 00000000000..46d56739e75 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validAddition.txt @@ -0,0 +1,5 @@ +FILE: validAddition.kt + public final fun foo(): R|kotlin/Unit| { + lvar x: R|kotlin/Int| = Int(0) + R|/x| = R|/x|.R|kotlin/Int.plus|(Int(1)) + } diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validSubtraction.kt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validSubtraction.kt new file mode 100644 index 00000000000..c2df85864a5 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validSubtraction.kt @@ -0,0 +1,4 @@ +fun foo() { + var x = 0 + x = x - 1 +} diff --git a/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validSubtraction.txt b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validSubtraction.txt new file mode 100644 index 00000000000..0ac6d53eb8b --- /dev/null +++ b/compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validSubtraction.txt @@ -0,0 +1,5 @@ +FILE: validSubtraction.kt + public final fun foo(): R|kotlin/Unit| { + lvar x: R|kotlin/Int| = Int(0) + R|/x| = R|/x|.R|kotlin/Int.minus|(Int(1)) + } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java index c23444ba612..603e246688b 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/ExtendedFirDiagnosticsTestGenerated.java @@ -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"); + } + } } 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 924f11bb942..d1feafae5e3 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 @@ -79,14 +79,18 @@ private class ComposedExpressionCheckers : ExpressionCheckers() { get() = _qualifiedAccessCheckers override val functionCallCheckers: List get() = _functionCallCheckers + override val variableAssignmentCheckers: List + get() = _variableAssignmentCheckers private val _expressionCheckers: MutableList = mutableListOf() private val _qualifiedAccessCheckers: MutableList = mutableListOf() private val _functionCallCheckers: MutableList = mutableListOf() + private val _variableAssignmentCheckers: MutableList = mutableListOf() fun register(checkers: ExpressionCheckers) { _expressionCheckers += checkers.allExpressionCheckers _qualifiedAccessCheckers += checkers.allQualifiedAccessCheckers _functionCallCheckers += checkers.allFunctionCallCheckers + _variableAssignmentCheckers += checkers.variableAssignmentCheckers } } 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 1b22c8cad1c..f26134cd146 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,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( diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExpressionCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExpressionCheckers.kt index 9cecd45eae3..1ea9f1f3c7d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExpressionCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExpressionCheckers.kt @@ -13,6 +13,7 @@ abstract class ExpressionCheckers { open val expressionCheckers: List = emptyList() open val qualifiedAccessCheckers: List = emptyList() open val functionCallCheckers: List = emptyList() + open val variableAssignmentCheckers: List = emptyList() internal val allExpressionCheckers get() = expressionCheckers internal val allQualifiedAccessCheckers get() = qualifiedAccessCheckers + allExpressionCheckers diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExtendedExpressionCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExtendedExpressionCheckers.kt new file mode 100644 index 00000000000..317a34800cf --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/ExtendedExpressionCheckers.kt @@ -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 = listOf( + CanBeReplacedWithOperatorAssignmentChecker + ) +} \ No newline at end of file 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 56bb7d01d7c..42727c0de00 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 @@ -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 { +abstract class FirExpressionChecker { abstract fun check(functionCall: E, context: CheckerContext, reporter: DiagnosticReporter) } -typealias FirBasicExpresionChecker = FirExpressionChecker +typealias FirBasicExpresionChecker = FirExpressionChecker typealias FirQualifiedAccessChecker = FirExpressionChecker typealias FirFunctionCallChecker = FirExpressionChecker +typealias FirVariableAssignmentChecker = FirExpressionChecker 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 new file mode 100644 index 00000000000..bcfdabdd4ec --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/CanBeReplacedWithOperatorAssignmentChecker.kt @@ -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() { + 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 +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt index a2db9b400ce..5bbca3548fd 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ExpressionCheckersDiagnosticComponent.kt @@ -79,7 +79,11 @@ class ExpressionCheckersDiagnosticComponent(collector: AbstractDiagnosticCollect runCheck { checkers.expressionCheckers.check(getClassCall, data, it) } } - private fun List>.check(expression: E, context: CheckerContext, reporter: DiagnosticReporter) { + override fun visitVariableAssignment(variableAssignment: FirVariableAssignment, data: CheckerContext) { + runCheck { checkers.variableAssignmentCheckers.check(variableAssignment, data, it) } + } + + private fun List>.check(expression: E, context: CheckerContext, reporter: DiagnosticReporter) { for (checker in this) { checker.check(expression, context, reporter) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index bd555b00c56..ada89c59c97 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -87,6 +87,7 @@ object FirErrors { val REDUNDANT_MODALITY_MODIFIER by warning0() val REDUNDANT_RETURN_UNIT_TYPE by warning0() val REDUNDANT_EXPLICIT_TYPE by warning0() + val CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT by warning0() } diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractExtendedFirDiagnosticsTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractExtendedFirDiagnosticsTest.kt index 9bc8ff76acd..a0f9b0626b3 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractExtendedFirDiagnosticsTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/fir/AbstractExtendedFirDiagnosticsTest.kt @@ -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) } } \ No newline at end of file