Skip to content

Commit

Permalink
NoSemicolonsRule: don't report semicolon for control flow with empty …
Browse files Browse the repository at this point in the history
…body (#976)

* NoSemicolonsRule: don't report semicolon for control flow with empty body

* Drop a comment

* Add comments pointing to issues

Co-authored-by: Roman Zavarnitsyn <rom4ek93@gmail.com>
  • Loading branch information
t-kameyama and romtsn authored Nov 28, 2020
1 parent 05a2c22 commit 4e1403e
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.pinterest.ktlint.ruleset.standard
import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.KDOC_TEXT
import com.pinterest.ktlint.core.ast.ElementType.OBJECT_KEYWORD
import com.pinterest.ktlint.core.ast.ElementType.SEMICOLON
import com.pinterest.ktlint.core.ast.isPartOf
import com.pinterest.ktlint.core.ast.isPartOfComment
import com.pinterest.ktlint.core.ast.isPartOfString
Expand All @@ -20,7 +21,10 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassBody
import org.jetbrains.kotlin.psi.KtDoWhileExpression
import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.psi.KtIfExpression
import org.jetbrains.kotlin.psi.KtLoopExpression
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType

class NoSemicolonsRule : Rule("no-semi") {
Expand All @@ -33,13 +37,14 @@ class NoSemicolonsRule : Rule("no-semi") {
if (node.elementType == KDOC_TEXT) {
return
}
if (node is LeafPsiElement && node.textMatches(";") && !node.isPartOfString() && !node.isPartOfEnumEntry()) {
if (node is LeafPsiElement &&
node.elementType == SEMICOLON &&
!node.isPartOfString() &&
!node.isPartOfEnumEntry()
) {
val nextLeaf = node.nextLeaf()
if (doesNotRequirePreSemi(nextLeaf)) {
if (node.prevCodeLeaf()?.elementType == OBJECT_KEYWORD) {
// https://github.com/shyiko/ktlint/issues/281
return
}
val prevCodeLeaf = node.prevCodeLeaf()
if (doesNotRequirePreSemi(nextLeaf) && doesNotRequirePostSemi(prevCodeLeaf)) {
emit(node.startOffset, "Unnecessary semicolon", true)
if (autoCorrect) {
node.treeParent.removeChild(node)
Expand Down Expand Up @@ -73,6 +78,22 @@ class NoSemicolonsRule : Rule("no-semi") {
return nextLeaf == null /* eof */
}

private fun doesNotRequirePostSemi(prevLeaf: ASTNode?): Boolean {
if (prevLeaf?.elementType == OBJECT_KEYWORD) {
// https://github.com/pinterest/ktlint/issues/281
return false
}
val parent = prevLeaf?.treeParent?.psi
if (parent is KtLoopExpression && parent !is KtDoWhileExpression && parent.body == null) {
// https://github.com/pinterest/ktlint/issues/955
return false
}
if (parent is KtIfExpression && parent.then == null) {
return false
}
return true
}

private fun ASTNode.isPartOfEnumEntry(): Boolean {
if (isPartOf(KtEnumEntry::class)) return true
val lBrace = prevLeaf { !it.isWhiteSpace() && !it.isPartOfComment() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,4 +230,60 @@ class NoSemicolonsRuleTest {
)
)
}

@Test
fun testForWithEmptyBody() {
assertThat(
NoSemicolonsRule().lint(
"""
fun test(list: List<Int>) {
for (i in list);
}
""".trimIndent()
)
).isEmpty()
}

@Test
fun testWhileWithEmptyBody() {
assertThat(
NoSemicolonsRule().lint(
"""
fun test() {
while (true);
}
""".trimIndent()
)
).isEmpty()
}

@Test
fun testDoWhileWithEmptyBody() {
assertThat(
NoSemicolonsRule().lint(
"""
fun test() {
do while (true);
}
""".trimIndent()
)
).isEqualTo(
listOf(
LintError(2, 20, "no-semi", "Unnecessary semicolon")
)
)
}

@Test
fun testIfWithEmptyBranch() {
assertThat(
NoSemicolonsRule().lint(
"""
fun testIf() {
if (true);
}
""".trimIndent()
)
).isEmpty()
}
}

0 comments on commit 4e1403e

Please # to comment.