-
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.
Rule 6.1.6 abstract classes should have at least one abstract method (#…
…457) rule-6.1.6-abstract-classes(#442) ### What's done: * Rule logic made * Added fix and warn tests
- Loading branch information
Showing
12 changed files
with
211 additions
and
1 deletion.
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
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
75 changes: 75 additions & 0 deletions
75
diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/AbstractClassesRule.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,75 @@ | ||
package org.cqfn.diktat.ruleset.rules.classes | ||
|
||
import com.pinterest.ktlint.core.Rule | ||
import com.pinterest.ktlint.core.ast.ElementType.ABSTRACT_KEYWORD | ||
import com.pinterest.ktlint.core.ast.ElementType.CLASS | ||
import com.pinterest.ktlint.core.ast.ElementType.CLASS_BODY | ||
import com.pinterest.ktlint.core.ast.ElementType.FUN | ||
import com.pinterest.ktlint.core.ast.ElementType.IDENTIFIER | ||
import com.pinterest.ktlint.core.ast.ElementType.MODIFIER_LIST | ||
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE | ||
import com.pinterest.ktlint.core.ast.isWhiteSpace | ||
import org.cqfn.diktat.common.config.rules.RulesConfig | ||
import org.cqfn.diktat.ruleset.constants.Warnings.CLASS_SHOULD_NOT_BE_ABSTRACT | ||
import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType | ||
import org.cqfn.diktat.ruleset.utils.getFirstChildWithType | ||
import org.cqfn.diktat.ruleset.utils.hasChildOfType | ||
import org.jetbrains.kotlin.com.intellij.lang.ASTNode | ||
|
||
/** | ||
* Checks if abstract class has any abstract method. If not, warns that class should not be abstract | ||
*/ | ||
class AbstractClassesRule(private val configRule: List<RulesConfig>) : Rule("abstract-classes") { | ||
private lateinit var emitWarn: ((offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit) | ||
private var isFixMode: Boolean = false | ||
|
||
override fun visit(node: ASTNode, | ||
autoCorrect: Boolean, | ||
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit) { | ||
emitWarn = emit | ||
isFixMode = autoCorrect | ||
|
||
if (node.elementType == CLASS) { | ||
val classBody = node.getFirstChildWithType(CLASS_BODY) ?: return | ||
|
||
if (hasAbstractModifier(node)) { | ||
handleAbstractClass(classBody, node) | ||
} | ||
} | ||
} | ||
|
||
private fun hasAbstractModifier(node: ASTNode): Boolean = | ||
node.getFirstChildWithType(MODIFIER_LIST)?.hasChildOfType(ABSTRACT_KEYWORD) ?: false | ||
|
||
@Suppress("UnsafeCallOnNullableType") | ||
private fun handleAbstractClass(node: ASTNode, classNode: ASTNode) { | ||
val functions = node.getAllChildrenWithType(FUN) | ||
|
||
val identifier = classNode.getFirstChildWithType(IDENTIFIER)!!.text | ||
|
||
if (functions.isNotEmpty() && functions.none { hasAbstractModifier(it) }) { | ||
CLASS_SHOULD_NOT_BE_ABSTRACT.warnAndFix(configRule, emitWarn, isFixMode, identifier, node.startOffset, node) { | ||
val modList = classNode.getFirstChildWithType(MODIFIER_LIST)!! | ||
if (modList.getChildren(null).size > 1) { | ||
val abstractKeyword = modList.getFirstChildWithType(ABSTRACT_KEYWORD)!! | ||
|
||
// we are deleting one keyword, so we need to delete extra space | ||
val spaceInModifiers = if (abstractKeyword == modList.firstChildNode) { | ||
abstractKeyword.treeNext | ||
} else { | ||
abstractKeyword.treePrev | ||
} | ||
modList.removeChild(abstractKeyword) | ||
if (spaceInModifiers != null && spaceInModifiers.isWhiteSpace()) { | ||
modList.removeChild(spaceInModifiers) | ||
} | ||
} else { | ||
if (modList.treeNext.isWhiteSpace()) { | ||
classNode.removeChild(modList.treeNext) | ||
} | ||
classNode.removeChild(modList) | ||
} | ||
} | ||
} | ||
} | ||
} |
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
16 changes: 16 additions & 0 deletions
16
diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter6/AbstractClassesFixTest.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,16 @@ | ||
package org.cqfn.diktat.ruleset.chapter6 | ||
|
||
import generated.WarningNames | ||
import generated.WarningNames.CLASS_SHOULD_NOT_BE_ABSTRACT | ||
import org.cqfn.diktat.util.FixTestBase | ||
import org.cqfn.diktat.ruleset.rules.classes.AbstractClassesRule | ||
import org.junit.jupiter.api.Tag | ||
import org.junit.jupiter.api.Test | ||
|
||
class AbstractClassesFixTest : FixTestBase("test/paragraph6/abstract_classes", ::AbstractClassesRule) { | ||
@Test | ||
@Tag(CLASS_SHOULD_NOT_BE_ABSTRACT) | ||
fun `fix abstract class`() { | ||
fixAndCompare("ShouldRemoveAbstractKeywordExpected.kt", "ShouldRemoveAbstractKeywordTest.kt") | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter6/AbstractClassesWarnTest.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,59 @@ | ||
package org.cqfn.diktat.ruleset.chapter6 | ||
|
||
import com.pinterest.ktlint.core.LintError | ||
import generated.WarningNames.CLASS_SHOULD_NOT_BE_ABSTRACT | ||
import org.cqfn.diktat.ruleset.constants.Warnings | ||
import org.cqfn.diktat.ruleset.rules.DIKTAT_RULE_SET_ID | ||
import org.cqfn.diktat.ruleset.rules.classes.AbstractClassesRule | ||
import org.cqfn.diktat.util.LintTestBase | ||
import org.junit.jupiter.api.Tag | ||
import org.junit.jupiter.api.Test | ||
|
||
class AbstractClassesWarnTest : LintTestBase(::AbstractClassesRule) { | ||
private val ruleId = "$DIKTAT_RULE_SET_ID:abstract-classes" | ||
|
||
@Test | ||
@Tag(CLASS_SHOULD_NOT_BE_ABSTRACT) | ||
fun `should not remove abstract`() { | ||
lintMethod( | ||
""" | ||
|abstract class Some(val a: Int = 5) { | ||
| abstract fun func() {} | ||
| | ||
| fun another() {} | ||
|} | ||
""".trimMargin() | ||
) | ||
} | ||
|
||
|
||
@Test | ||
@Tag(CLASS_SHOULD_NOT_BE_ABSTRACT) | ||
fun `should remove abstract`() { | ||
lintMethod( | ||
""" | ||
|abstract class Some(val a: Int = 5) { | ||
| fun func() {} | ||
|} | ||
""".trimMargin(), | ||
LintError(1, 37, ruleId, "${Warnings.CLASS_SHOULD_NOT_BE_ABSTRACT.warnText()} Some", true) | ||
) | ||
} | ||
|
||
@Test | ||
@Tag(CLASS_SHOULD_NOT_BE_ABSTRACT) | ||
fun `should remove abstract with inner`() { | ||
lintMethod( | ||
""" | ||
|class Some(val a: Int = 5) { | ||
| fun func() {} | ||
| | ||
| inner abstract class Inner { | ||
| fun another() | ||
| } | ||
|} | ||
""".trimMargin(), | ||
LintError(4, 32, ruleId, "${Warnings.CLASS_SHOULD_NOT_BE_ABSTRACT.warnText()} Inner", true) | ||
) | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
...rc/test/resources/test/paragraph6/abstract_classes/ShouldRemoveAbstractKeywordExpected.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,21 @@ | ||
package test.paragraph6.abstract_classes | ||
|
||
class Some() { | ||
fun some(){} | ||
|
||
fun another(){} | ||
|
||
@SomeAnnotation @Another inner class Any { | ||
fun func(){} | ||
} | ||
|
||
inner class Second { | ||
fun someFunc(){} | ||
} | ||
} | ||
|
||
abstract class Another { | ||
abstract fun absFunc() | ||
|
||
fun someFunc(){} | ||
} |
21 changes: 21 additions & 0 deletions
21
...es/src/test/resources/test/paragraph6/abstract_classes/ShouldRemoveAbstractKeywordTest.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,21 @@ | ||
package test.paragraph6.abstract_classes | ||
|
||
abstract class Some() { | ||
fun some(){} | ||
|
||
fun another(){} | ||
|
||
@SomeAnnotation @Another abstract inner class Any { | ||
fun func(){} | ||
} | ||
|
||
inner abstract class Second { | ||
fun someFunc(){} | ||
} | ||
} | ||
|
||
abstract class Another { | ||
abstract fun absFunc() | ||
|
||
fun someFunc(){} | ||
} |
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
f28dfb9
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't able to retrieve PDD puzzles from the code base and submit them to GitHub. If you think that it's a bug on our side, please submit it to yegor256/0pdd:
Please, copy and paste this stack trace to GitHub: