-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
### What's done: - Implemented "Range to until" inspection
- Loading branch information
Showing
14 changed files
with
292 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 116 additions & 0 deletions
116
diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/RangeConventionalRule.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package org.cqfn.diktat.ruleset.rules.chapter3 | ||
|
||
import org.cqfn.diktat.common.config.rules.RuleConfiguration | ||
import org.cqfn.diktat.common.config.rules.RulesConfig | ||
import org.cqfn.diktat.common.config.rules.getRuleConfig | ||
import org.cqfn.diktat.ruleset.constants.Warnings.CONVENTIONAL_RANGE | ||
import org.cqfn.diktat.ruleset.rules.DiktatRule | ||
import org.cqfn.diktat.ruleset.utils.KotlinParser | ||
import org.cqfn.diktat.ruleset.utils.getIdentifierName | ||
import org.cqfn.diktat.ruleset.utils.hasChildOfType | ||
import org.cqfn.diktat.ruleset.utils.takeByChainOfTypes | ||
|
||
import com.pinterest.ktlint.core.ast.ElementType.BINARY_EXPRESSION | ||
import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION | ||
import com.pinterest.ktlint.core.ast.ElementType.IDENTIFIER | ||
import com.pinterest.ktlint.core.ast.ElementType.MINUS | ||
import com.pinterest.ktlint.core.ast.ElementType.RANGE | ||
import com.pinterest.ktlint.core.ast.ElementType.REFERENCE_EXPRESSION | ||
import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT | ||
import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST | ||
import com.pinterest.ktlint.core.ast.isWhiteSpace | ||
import com.pinterest.ktlint.core.ast.parent | ||
import org.jetbrains.kotlin.com.intellij.lang.ASTNode | ||
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement | ||
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl | ||
import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet | ||
import org.jetbrains.kotlin.psi.KtBinaryExpression | ||
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression | ||
|
||
/** | ||
* This rule warn and fix cases when it possible to replace range with until or replace rangeTo function with range | ||
*/ | ||
@Suppress("UnsafeCallOnNullableType") | ||
class RangeConventionalRule(configRules: List<RulesConfig>) : DiktatRule( | ||
"range", | ||
configRules, | ||
listOf(CONVENTIONAL_RANGE) | ||
) { | ||
private val configuration by lazy { | ||
RangeConventionalConfiguration( | ||
this.configRules.getRuleConfig(CONVENTIONAL_RANGE)?.configuration ?: emptyMap(), | ||
) | ||
} | ||
override fun logic(node: ASTNode) { | ||
if (node.elementType == DOT_QUALIFIED_EXPRESSION && !configuration.isRangeToIgnore) { | ||
handleQualifiedExpression(node) | ||
} | ||
if (node.elementType == RANGE) { | ||
handleRange(node) | ||
} | ||
} | ||
|
||
@Suppress("TOO_MANY_LINES_IN_LAMBDA") | ||
private fun handleQualifiedExpression(node: ASTNode) { | ||
(node.psi as KtDotQualifiedExpression).selectorExpression?.node?.let { | ||
if (it.findChildByType(REFERENCE_EXPRESSION)?.getIdentifierName()?.text == "rangeTo") { | ||
val arguments = it.findChildByType(VALUE_ARGUMENT_LIST)?.getChildren(TokenSet.create(VALUE_ARGUMENT)) | ||
if (arguments?.size == 1) { | ||
CONVENTIONAL_RANGE.warnAndFix(configRules, emitWarn, isFixMode, "replace `rangeTo` with `..`: ${node.text}", node.startOffset, node) { | ||
val receiverExpression = (node.psi as KtDotQualifiedExpression).receiverExpression.text | ||
val correctNode = KotlinParser().createNode("$receiverExpression..${arguments[0].text}") | ||
node.treeParent.addChild(correctNode, node) | ||
node.treeParent.removeChild(node) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Suppress("TOO_MANY_LINES_IN_LAMBDA") | ||
private fun handleRange(node: ASTNode) { | ||
val binaryInExpression = (node.parent({ it.elementType == BINARY_EXPRESSION })?.psi as KtBinaryExpression?) | ||
(binaryInExpression | ||
?.right | ||
?.node | ||
?.takeByChainOfTypes(BINARY_EXPRESSION) | ||
?.psi as KtBinaryExpression?) | ||
?.let { | ||
if (it.operationReference.node.hasChildOfType(MINUS)) { | ||
val errorNode = binaryInExpression!!.node | ||
CONVENTIONAL_RANGE.warnAndFix(configRules, emitWarn, isFixMode, "replace `..` with `until`: ${errorNode.text}", errorNode.startOffset, errorNode) { | ||
replaceUntil(node) | ||
// fix right side of binary expression to correct form (remove ` - 1 `) : (b-1) -> (b) | ||
val astNode = it.node | ||
val parent = astNode.treeParent | ||
parent.addChild(astNode.firstChildNode, astNode) | ||
parent.removeChild(astNode) | ||
} | ||
} | ||
} | ||
} | ||
|
||
private fun replaceUntil(node: ASTNode) { | ||
val untilNode = LeafPsiElement(IDENTIFIER, "until") | ||
val parent = node.treeParent | ||
if (parent.treePrev?.isWhiteSpace() != true) { | ||
parent.treeParent.addChild(PsiWhiteSpaceImpl(" "), parent) | ||
} | ||
if (parent.treeNext?.isWhiteSpace() != true) { | ||
parent.treeParent.addChild(PsiWhiteSpaceImpl(" "), parent.treeNext) | ||
} | ||
parent.addChild(untilNode, node) | ||
parent.removeChild(node) | ||
} | ||
|
||
/** | ||
* | ||
* [RuleConfiguration] for rangeTo function | ||
*/ | ||
class RangeConventionalConfiguration(config: Map<String, String>) : RuleConfiguration(config) { | ||
/** | ||
* Does ignore rangeTo function | ||
*/ | ||
val isRangeToIgnore = config["isRangeToIgnore"]?.toBoolean() ?: false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
...at-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/RangeConventionalRuleFixTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package org.cqfn.diktat.ruleset.chapter3 | ||
|
||
import org.cqfn.diktat.ruleset.rules.chapter3.RangeConventionalRule | ||
import org.cqfn.diktat.util.FixTestBase | ||
import org.junit.jupiter.api.Test | ||
|
||
class RangeConventionalRuleFixTest : FixTestBase("test/paragraph3/range", ::RangeConventionalRule) { | ||
@Test | ||
fun `should fix with until`() { | ||
fixAndCompare("RangeToUntilExpected.kt", "RangeToUntilTest.kt") | ||
} | ||
|
||
@Test | ||
fun `should fix with rangeTo`() { | ||
fixAndCompare("RangeToExpected.kt", "RangeToTest.kt") | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
...t-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/RangeConventionalRuleWarnTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package org.cqfn.diktat.ruleset.chapter3 | ||
|
||
import org.cqfn.diktat.common.config.rules.RulesConfig | ||
import org.cqfn.diktat.ruleset.constants.Warnings | ||
import org.cqfn.diktat.ruleset.rules.DIKTAT_RULE_SET_ID | ||
import org.cqfn.diktat.ruleset.rules.chapter3.RangeConventionalRule | ||
import org.cqfn.diktat.util.LintTestBase | ||
|
||
import com.pinterest.ktlint.core.LintError | ||
import org.junit.jupiter.api.Test | ||
|
||
class RangeConventionalRuleWarnTest : LintTestBase(::RangeConventionalRule) { | ||
private val ruleId = "$DIKTAT_RULE_SET_ID:range" | ||
private val rulesConfigRangeRule: List<RulesConfig> = listOf( | ||
RulesConfig( | ||
Warnings.CONVENTIONAL_RANGE.name, true, | ||
mapOf("isRangeToIgnore" to "true")) | ||
) | ||
|
||
@Test | ||
fun `check simple examples with until`() { | ||
lintMethod( | ||
""" | ||
|fun foo() { | ||
| for (i in 1..(4 - 1)) print(i) | ||
| for (i in 1..(b - 1)) print(i) | ||
| for (i in ((1 .. ((4 - 1))))) print(i) | ||
| for (i in 4 downTo 1) print(i) | ||
| for (i in 1..4) print(i) | ||
| for (i in 1..4 step 2) print(i) | ||
| for (i in 4 downTo 1 step 3) print(i) | ||
| if (6 in (1..10) && true) {} | ||
| for (i in 1..(4 - 1) step 3) print(i) | ||
|} | ||
""".trimMargin(), | ||
LintError(2, 15, ruleId, "${Warnings.CONVENTIONAL_RANGE.warnText()} replace `..` with `until`: 1..(4 - 1)", true), | ||
LintError(3, 15, ruleId, "${Warnings.CONVENTIONAL_RANGE.warnText()} replace `..` with `until`: 1..(b - 1)", true), | ||
LintError(4, 17, ruleId, "${Warnings.CONVENTIONAL_RANGE.warnText()} replace `..` with `until`: 1 .. ((4 - 1))", true), | ||
LintError(10, 15, ruleId, "${Warnings.CONVENTIONAL_RANGE.warnText()} replace `..` with `until`: 1..(4 - 1)", true) | ||
) | ||
} | ||
|
||
@Test | ||
fun `check simple examples with rangeTo`() { | ||
lintMethod( | ||
""" | ||
|fun foo() { | ||
| val num = 1 | ||
| val w = num.rangeTo(num, num) | ||
| val q = 1..5 | ||
| val w = num.rangeTo(num) | ||
|} | ||
""".trimMargin(), | ||
LintError(5, 13, ruleId, "${Warnings.CONVENTIONAL_RANGE.warnText()} replace `rangeTo` with `..`: num.rangeTo(num)", true) | ||
) | ||
} | ||
|
||
@Test | ||
fun `check simple examples with rangeTo with config`() { | ||
lintMethod( | ||
""" | ||
|fun foo() { | ||
| val w = num.rangeTo(num, num) | ||
| val w = num.rangeTo(num) | ||
|} | ||
""".trimMargin(), | ||
rulesConfigList = rulesConfigRangeRule | ||
) | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
diktat-rules/src/test/resources/test/paragraph3/range/RangeToExpected.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package test.paragraph3.range | ||
|
||
fun foo() { | ||
val num = 1 | ||
val w = num.rangeTo(num, num) | ||
val q = 1..5 | ||
val e = num..num | ||
if (1 in num..10) { } | ||
} |
9 changes: 9 additions & 0 deletions
9
diktat-rules/src/test/resources/test/paragraph3/range/RangeToTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package test.paragraph3.range | ||
|
||
fun foo() { | ||
val num = 1 | ||
val w = num.rangeTo(num, num) | ||
val q = 1..5 | ||
val e = num.rangeTo(num) | ||
if (1 in num.rangeTo(10)) { } | ||
} |
15 changes: 15 additions & 0 deletions
15
diktat-rules/src/test/resources/test/paragraph3/range/RangeToUntilExpected.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package test.paragraph3.range | ||
|
||
class A { | ||
fun foo() { | ||
for (i in 1..4) print(i) | ||
for (i in 4 downTo 1) print(i) | ||
for (i in 1 until 4) print(i) | ||
for (i in 1..4 step 2) print(i) | ||
for (i in 4 downTo 1 step 3) print(i) | ||
if (6 in (1..10) && true) {} | ||
for (i in 1 until (4)) print(i) | ||
for (i in 1 until (b)) print(i) | ||
for (i in ((1 until ((4))))) print(i) | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
diktat-rules/src/test/resources/test/paragraph3/range/RangeToUntilTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package test.paragraph3.range | ||
|
||
class A { | ||
fun foo() { | ||
for (i in 1..4) print(i) | ||
for (i in 4 downTo 1) print(i) | ||
for (i in 1 until 4) print(i) | ||
for (i in 1..4 step 2) print(i) | ||
for (i in 4 downTo 1 step 3) print(i) | ||
if (6 in (1..10) && true) {} | ||
for (i in 1..(4 - 1)) print(i) | ||
for (i in 1..(b - 1)) print(i) | ||
for (i in ((1 .. ((4 - 1))))) print(i) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters