mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-29 08:31:29 +00:00
[FIR] Add empty range checker
This commit is contained in:
29
compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker/NoWarning.kt
vendored
Normal file
29
compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker/NoWarning.kt
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
for (i in 1..2) { }
|
||||
|
||||
val a = 3..4
|
||||
|
||||
val v = 1
|
||||
if (v in 5..6) { }
|
||||
}
|
||||
|
||||
|
||||
fun backward() {
|
||||
for (i in 2 downTo 1) { }
|
||||
|
||||
val a = 4 downTo 3
|
||||
|
||||
val v = 1
|
||||
if (v in -5 downTo -6) { }
|
||||
}
|
||||
|
||||
fun until() {
|
||||
for (i in 1 until 2) { }
|
||||
|
||||
val a = 3 until 4
|
||||
|
||||
val v = 1
|
||||
if (v in -5 until -4) { }
|
||||
}
|
||||
43
compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker/NoWarning.txt
vendored
Normal file
43
compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker/NoWarning.txt
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
FILE: NoWarning.kt
|
||||
public final fun foo(): R|kotlin/Unit| {
|
||||
lval <iterator>: R|kotlin/collections/IntIterator| = Int(1).R|kotlin/Int.rangeTo|(Int(2)).R|kotlin/ranges/IntProgression.iterator|()
|
||||
while(R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()) {
|
||||
lval i: R|kotlin/Int| = R|<local>/<iterator>|.R|kotlin/collections/IntIterator.next|()
|
||||
}
|
||||
|
||||
lval a: R|kotlin/ranges/IntRange| = Int(3).R|kotlin/Int.rangeTo|(Int(4))
|
||||
lval v: R|kotlin/Int| = Int(1)
|
||||
when () {
|
||||
Int(5).R|kotlin/Int.rangeTo|(Int(6)).R|kotlin/ranges/IntRange.contains|(R|<local>/v|) -> {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public final fun backward(): R|kotlin/Unit| {
|
||||
lval <iterator>: R|kotlin/collections/IntIterator| = Int(2).R|kotlin/ranges/downTo|(Int(1)).R|kotlin/ranges/IntProgression.iterator|()
|
||||
while(R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()) {
|
||||
lval i: R|kotlin/Int| = R|<local>/<iterator>|.R|kotlin/collections/IntIterator.next|()
|
||||
}
|
||||
|
||||
lval a: R|kotlin/ranges/IntProgression| = Int(4).R|kotlin/ranges/downTo|(Int(3))
|
||||
lval v: R|kotlin/Int| = Int(1)
|
||||
when () {
|
||||
Int(5).R|kotlin/Int.unaryMinus|().R|kotlin/ranges/downTo|(Int(6).R|kotlin/Int.unaryMinus|()).R|kotlin/collections/contains|<R|kotlin/Int|>(R|<local>/v|) -> {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public final fun until(): R|kotlin/Unit| {
|
||||
lval <iterator>: R|kotlin/collections/IntIterator| = Int(1).R|kotlin/ranges/until|(Int(2)).R|kotlin/ranges/IntProgression.iterator|()
|
||||
while(R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()) {
|
||||
lval i: R|kotlin/Int| = R|<local>/<iterator>|.R|kotlin/collections/IntIterator.next|()
|
||||
}
|
||||
|
||||
lval a: R|kotlin/ranges/IntRange| = Int(3).R|kotlin/ranges/until|(Int(4))
|
||||
lval v: R|kotlin/Int| = Int(1)
|
||||
when () {
|
||||
Int(5).R|kotlin/Int.unaryMinus|().R|kotlin/ranges/until|(Int(4).R|kotlin/Int.unaryMinus|()).R|kotlin/ranges/IntRange.contains|(R|<local>/v|) -> {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
28
compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker/Warning.kt
vendored
Normal file
28
compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker/Warning.kt
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
for (i in <!EMPTY_RANGE!>2..1<!>) { }
|
||||
|
||||
val a = <!EMPTY_RANGE!>10..0<!>
|
||||
|
||||
val v = 1
|
||||
if (v in <!EMPTY_RANGE!>10..1<!>) { }
|
||||
}
|
||||
|
||||
fun backward() {
|
||||
for (i in <!EMPTY_RANGE!>1 downTo 2<!>) { }
|
||||
|
||||
val a = <!EMPTY_RANGE!>-3 downTo 4<!>
|
||||
|
||||
val v = 1
|
||||
if (v in <!EMPTY_RANGE!>0 downTo 6<!>) { }
|
||||
}
|
||||
|
||||
fun until() {
|
||||
for (i in <!EMPTY_RANGE!>1 until 1<!>) { }
|
||||
|
||||
val a = <!EMPTY_RANGE!>4 until 3<!>
|
||||
|
||||
val v = 1
|
||||
if (v in <!EMPTY_RANGE!>-5 until -5<!>) { }
|
||||
}
|
||||
43
compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker/Warning.txt
vendored
Normal file
43
compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker/Warning.txt
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
FILE: Warning.kt
|
||||
public final fun foo(): R|kotlin/Unit| {
|
||||
lval <iterator>: R|kotlin/collections/IntIterator| = Int(2).R|kotlin/Int.rangeTo|(Int(1)).R|kotlin/ranges/IntProgression.iterator|()
|
||||
while(R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()) {
|
||||
lval i: R|kotlin/Int| = R|<local>/<iterator>|.R|kotlin/collections/IntIterator.next|()
|
||||
}
|
||||
|
||||
lval a: R|kotlin/ranges/IntRange| = Int(10).R|kotlin/Int.rangeTo|(Int(0))
|
||||
lval v: R|kotlin/Int| = Int(1)
|
||||
when () {
|
||||
Int(10).R|kotlin/Int.rangeTo|(Int(1)).R|kotlin/ranges/IntRange.contains|(R|<local>/v|) -> {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public final fun backward(): R|kotlin/Unit| {
|
||||
lval <iterator>: R|kotlin/collections/IntIterator| = Int(1).R|kotlin/ranges/downTo|(Int(2)).R|kotlin/ranges/IntProgression.iterator|()
|
||||
while(R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()) {
|
||||
lval i: R|kotlin/Int| = R|<local>/<iterator>|.R|kotlin/collections/IntIterator.next|()
|
||||
}
|
||||
|
||||
lval a: R|kotlin/ranges/IntProgression| = Int(3).R|kotlin/Int.unaryMinus|().R|kotlin/ranges/downTo|(Int(4))
|
||||
lval v: R|kotlin/Int| = Int(1)
|
||||
when () {
|
||||
Int(0).R|kotlin/ranges/downTo|(Int(6)).R|kotlin/collections/contains|<R|kotlin/Int|>(R|<local>/v|) -> {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public final fun until(): R|kotlin/Unit| {
|
||||
lval <iterator>: R|kotlin/collections/IntIterator| = Int(1).R|kotlin/ranges/until|(Int(1)).R|kotlin/ranges/IntProgression.iterator|()
|
||||
while(R|<local>/<iterator>|.R|kotlin/collections/Iterator.hasNext|()) {
|
||||
lval i: R|kotlin/Int| = R|<local>/<iterator>|.R|kotlin/collections/IntIterator.next|()
|
||||
}
|
||||
|
||||
lval a: R|kotlin/ranges/IntRange| = Int(4).R|kotlin/ranges/until|(Int(3))
|
||||
lval v: R|kotlin/Int| = Int(1)
|
||||
when () {
|
||||
Int(5).R|kotlin/Int.unaryMinus|().R|kotlin/ranges/until|(Int(5).R|kotlin/Int.unaryMinus|()).R|kotlin/ranges/IntRange.contains|(R|<local>/v|) -> {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -165,4 +165,27 @@ public class ExtendedFirDiagnosticsTestGenerated extends AbstractExtendedFirDiag
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/canBeReplacedWithOperatorAssignment/validSubtraction.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class EmptyRangeChecker extends AbstractExtendedFirDiagnosticsTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInEmptyRangeChecker() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@TestMetadata("NoWarning.kt")
|
||||
public void testNoWarning() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker/NoWarning.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Warning.kt")
|
||||
public void testWarning() throws Exception {
|
||||
runTest("compiler/fir/analysis-tests/testData/extendedCheckers/emptyRangeChecker/Warning.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,14 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression
|
||||
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.extended.ArrayEqualityCanBeReplacedWithEquals
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.extended.CanBeReplacedWithOperatorAssignmentChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.extended.EmptyRangeChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantSingleExpressionStringTemplateChecker
|
||||
|
||||
object ExtendedExpressionCheckers : ExpressionCheckers() {
|
||||
override val expressionCheckers: List<FirBasicExpresionChecker> = listOf(
|
||||
ArrayEqualityCanBeReplacedWithEquals,
|
||||
RedundantSingleExpressionStringTemplateChecker
|
||||
RedundantSingleExpressionStringTemplateChecker,
|
||||
EmptyRangeChecker
|
||||
)
|
||||
override val variableAssignmentCheckers: List<FirVariableAssignmentChecker> = listOf(
|
||||
CanBeReplacedWithOperatorAssignmentChecker
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 org.jetbrains.kotlin.fir.FirFakeSourceElement
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpresionChecker
|
||||
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.FirStatement
|
||||
import org.jetbrains.kotlin.fir.psi
|
||||
|
||||
object EmptyRangeChecker : FirBasicExpresionChecker() {
|
||||
override fun check(functionCall: FirStatement, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
if (functionCall.source is FirFakeSourceElement<*>) return
|
||||
if (functionCall !is FirFunctionCall) return
|
||||
val range = functionCall.psi ?: return
|
||||
val left = range.children.getOrNull(0)?.text?.toLongOrNull() ?: return
|
||||
val right = range.children.getOrNull(2)?.text?.toLongOrNull() ?: return
|
||||
|
||||
val needReport = when (functionCall.calleeReference.name.asString()) {
|
||||
"rangeTo" -> {
|
||||
left > right
|
||||
}
|
||||
"downTo" -> {
|
||||
right > left
|
||||
}
|
||||
"until" -> {
|
||||
left >= right
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
if (needReport) {
|
||||
reporter.report(functionCall.source, FirErrors.EMPTY_RANGE)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -93,6 +93,7 @@ object FirErrors {
|
||||
val CAN_BE_VAL by warning0<FirSourceElement, PsiElement>()
|
||||
val CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT by warning0<FirSourceElement, PsiElement>()
|
||||
val ARRAY_EQUALITY_OPERATOR_CAN_BE_REPLACED_WITH_EQUALS by warning0<FirSourceElement, PsiElement>()
|
||||
val EMPTY_RANGE by warning0<FirSourceElement, PsiElement>()
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user