Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature. Ktlint rule wrapper #744

Merged
merged 14 commits into from
Feb 4, 2021
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.cqfn.diktat.ruleset.rules

import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.common.config.rules.isRuleEnabled
import org.cqfn.diktat.ruleset.constants.EmitType
import org.cqfn.diktat.ruleset.utils.log

import com.pinterest.ktlint.core.Rule
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import kotlin.system.exitProcess

private typealias DiktatConfigRule = org.cqfn.diktat.common.config.rules.Rule

/**
* This is a wrapper around Ktlint Rule
*
* @param id id of the rule
* @property configRules all rules from configuration
* @property inspections warnings that are used in the rule's code
*/
@Suppress("TooGenericExceptionCaught")
abstract class DiktatRule(id: String,
val configRules: List<RulesConfig>,
val inspections: List<DiktatConfigRule>) : Rule(id) {
/**
* Default value is false
*/
var isFixMode: Boolean = false

/**
* Will be initialized in visit
*/
lateinit var emitWarn: EmitType

override fun visit(node: ASTNode,
autoCorrect: Boolean,
emit: EmitType) {
emitWarn = emit
isFixMode = autoCorrect

if (areInspectionsDisabled()) {
return
} else {
try {
logic(node)
} catch (internalError: Throwable) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

damn, catching all exceptions? I do not know if it is a good idea, @petertrr think about this here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, isn't this what we were trying to achieve? Otherwise these exceptions are caught by ktlint and they just log a warning as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am saying that simply doing catch (Exception e) is a bad practice. May be there are different types of exceptions in ktlint to handle BEFORE we will catch all remaining exeptions

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything that is thrown from rule.visit is catched in Ktlint.kt with catch (e: Exception). So their idea is that the visitor either handles all exceptions, or they are considered internal errors.

log.error("Internal error has occurred in $id. Please make an issue on this bug at https://github.com/cqfn/diKTat/.", internalError)
log.error("As a workaround you can disable these inspections in yml config: $inspections")
exitProcess(1)
}
}
}

private fun areInspectionsDisabled(): Boolean =
inspections.none { configRules.isRuleEnabled(it) }

/**
* Logic of the rule
*
* @param node node that are coming from visit
*/
abstract fun logic(node: ASTNode)
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package org.cqfn.diktat.ruleset.rules.chapter1

import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.EmitType
import org.cqfn.diktat.ruleset.constants.Warnings.FILE_NAME_INCORRECT
import org.cqfn.diktat.ruleset.constants.Warnings.FILE_NAME_MATCH_CLASS
import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType
import org.cqfn.diktat.ruleset.utils.getFilePath
import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
import org.cqfn.diktat.ruleset.utils.isKotlinScript
import org.cqfn.diktat.ruleset.utils.isPascalCase

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.CLASS
import com.pinterest.ktlint.core.ast.ElementType.FILE
import com.pinterest.ktlint.core.ast.ElementType.IDENTIFIER
Expand All @@ -27,17 +26,10 @@ import java.io.File
* Aggressive: In case file contains only one class on upper level - it should be named with the same name
*/
@Suppress("ForbiddenComment")
class FileNaming(private val configRules: List<RulesConfig>) : Rule("file-naming") {
private var isFixMode: Boolean = false
private lateinit var emitWarn: EmitType
class FileNaming(configRules: List<RulesConfig>) : DiktatRule("file-naming", configRules, listOf(FILE_NAME_INCORRECT, FILE_NAME_MATCH_CLASS)) {
private lateinit var filePath: String

override fun visit(node: ASTNode,
autoCorrect: Boolean,
emit: EmitType) {
emitWarn = emit
isFixMode = autoCorrect

override fun logic(node: ASTNode) {
if (node.elementType == FILE) {
filePath = node.getFilePath()
if (!filePath.isKotlinScript()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package org.cqfn.diktat.ruleset.rules.chapter1
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.EmitType
import org.cqfn.diktat.ruleset.constants.Warnings.BACKTICKS_PROHIBITED
import org.cqfn.diktat.ruleset.constants.Warnings.CLASS_NAME_INCORRECT
import org.cqfn.diktat.ruleset.constants.Warnings.CONFUSING_IDENTIFIER_NAMING
Expand All @@ -18,6 +17,7 @@ import org.cqfn.diktat.ruleset.constants.Warnings.OBJECT_NAME_INCORRECT
import org.cqfn.diktat.ruleset.constants.Warnings.VARIABLE_HAS_PREFIX
import org.cqfn.diktat.ruleset.constants.Warnings.VARIABLE_NAME_INCORRECT
import org.cqfn.diktat.ruleset.constants.Warnings.VARIABLE_NAME_INCORRECT_FORMAT
import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.utils.Style
import org.cqfn.diktat.ruleset.utils.containsOneLetterOrZero
import org.cqfn.diktat.ruleset.utils.findChildAfter
Expand All @@ -42,7 +42,6 @@ import org.cqfn.diktat.ruleset.utils.toLowerCamelCase
import org.cqfn.diktat.ruleset.utils.toPascalCase
import org.cqfn.diktat.ruleset.utils.toUpperSnakeCase

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType
import com.pinterest.ktlint.core.ast.ElementType.CATCH
import com.pinterest.ktlint.core.ast.ElementType.CATCH_KEYWORD
Expand Down Expand Up @@ -80,7 +79,11 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
* // FixMe: because it fixes only declaration without the usages
*/
@Suppress("ForbiddenComment", "MISSING_KDOC_CLASS_ELEMENTS")
class IdentifierNaming(private val configRules: List<RulesConfig>) : Rule("identifier-naming") {
class IdentifierNaming(configRules: List<RulesConfig>) : DiktatRule("identifier-naming", configRules,
listOf(BACKTICKS_PROHIBITED, VARIABLE_NAME_INCORRECT, VARIABLE_NAME_INCORRECT_FORMAT, CONSTANT_UPPERCASE,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this pass diktat formatting rule?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

possible bug in diktat?

VARIABLE_HAS_PREFIX, CONFUSING_IDENTIFIER_NAMING, GENERIC_NAME, CLASS_NAME_INCORRECT,
ENUM_VALUE, EXCEPTION_SUFFIX, FUNCTION_BOOLEAN_PREFIX, FUNCTION_NAME_INCORRECT_CASE,
IDENTIFIER_LENGTH, OBJECT_NAME_INCORRECT)) {
private val allMethodPrefixes by lazy {
if (configuration.allowedBooleanPrefixes.isNullOrEmpty()) {
booleanMethodPrefixes
Expand All @@ -93,17 +96,10 @@ class IdentifierNaming(private val configRules: List<RulesConfig>) : Rule("ident
this.configRules.getRuleConfig(FUNCTION_BOOLEAN_PREFIX)?.configuration ?: emptyMap()
)
}
private var isFixMode: Boolean = false
private lateinit var emitWarn: EmitType

override fun visit(
node: ASTNode,
autoCorrect: Boolean,
emit: EmitType
override fun logic(
node: ASTNode
) {
isFixMode = autoCorrect
emitWarn = emit

// backticks are prohibited for identifier declarations everywhere except test methods that are marked with @Test annotation
if (isIdentifierWithBackticks(node)) {
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ package org.cqfn.diktat.ruleset.rules.chapter1

import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.common.config.rules.getCommonConfiguration
import org.cqfn.diktat.ruleset.constants.EmitType
import org.cqfn.diktat.ruleset.constants.Warnings.INCORRECT_PACKAGE_SEPARATOR
import org.cqfn.diktat.ruleset.constants.Warnings.PACKAGE_NAME_INCORRECT_CASE
import org.cqfn.diktat.ruleset.constants.Warnings.PACKAGE_NAME_INCORRECT_PATH
import org.cqfn.diktat.ruleset.constants.Warnings.PACKAGE_NAME_INCORRECT_PREFIX
import org.cqfn.diktat.ruleset.constants.Warnings.PACKAGE_NAME_INCORRECT_SYMBOLS
import org.cqfn.diktat.ruleset.constants.Warnings.PACKAGE_NAME_MISSING
import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.utils.*

import com.pinterest.ktlint.core.Rule
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.PACKAGE_DIRECTIVE
Expand All @@ -34,17 +33,12 @@ import java.util.concurrent.atomic.AtomicInteger
* package a.b.c.D -> then class D should be placed in a/b/c/ directories
*/
@Suppress("ForbiddenComment", "TOO_MANY_LINES_IN_LAMBDA")
class PackageNaming(private val configRules: List<RulesConfig>) : Rule("package-naming") {
private var isFixMode: Boolean = false
private lateinit var emitWarn: EmitType
class PackageNaming(configRules: List<RulesConfig>) : DiktatRule("package-naming", configRules,
listOf(INCORRECT_PACKAGE_SEPARATOR, PACKAGE_NAME_INCORRECT_CASE, PACKAGE_NAME_MISSING,
PACKAGE_NAME_INCORRECT_PATH, PACKAGE_NAME_INCORRECT_PREFIX, PACKAGE_NAME_INCORRECT_SYMBOLS)) {
private lateinit var domainName: String

override fun visit(node: ASTNode,
autoCorrect: Boolean,
emit: EmitType) {
isFixMode = autoCorrect
emitWarn = emit

override fun logic(node: ASTNode) {
val configuration by configRules.getCommonConfiguration()
configuration.domainName?.let {
domainName = it
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package org.cqfn.diktat.ruleset.rules.chapter2.comments

import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.EmitType
import org.cqfn.diktat.ruleset.constants.ListOfPairs
import org.cqfn.diktat.ruleset.constants.Warnings.COMMENTED_OUT_CODE
import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.BLOCK_COMMENT
import com.pinterest.ktlint.core.ast.ElementType.EOL_COMMENT
import com.pinterest.ktlint.core.ast.ElementType.FILE
Expand All @@ -24,18 +23,10 @@ import org.jetbrains.kotlin.resolve.ImportPath
* No commented out code is allowed, including imports.
*/
@Suppress("ForbiddenComment")
class CommentsRule(private val configRules: List<RulesConfig>) : Rule("comments") {
private var isFixMode: Boolean = false
private lateinit var emitWarn: EmitType
class CommentsRule(configRules: List<RulesConfig>) : DiktatRule("comments", configRules, listOf(COMMENTED_OUT_CODE)) {
private lateinit var ktPsiFactory: KtPsiFactory

override fun visit(node: ASTNode,
autoCorrect: Boolean,
emit: EmitType
) {
emitWarn = emit
isFixMode = autoCorrect

override fun logic(node: ASTNode) {
ktPsiFactory = KtPsiFactory(node.psi.project, false) // regarding markGenerated see KDoc in Kotlin sources
if (node.elementType == FILE) {
checkCommentedCode(node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package org.cqfn.diktat.ruleset.rules.chapter2.comments
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.EmitType
import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE
import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_MISSING_OR_WRONG_COPYRIGHT
import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_NOT_BEFORE_PACKAGE
import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_WRONG_FORMAT
import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_COPYRIGHT_YEAR
import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.utils.copyrightWords
import org.cqfn.diktat.ruleset.utils.findChildAfter
import org.cqfn.diktat.ruleset.utils.findChildBefore
Expand All @@ -17,7 +17,6 @@ import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
import org.cqfn.diktat.ruleset.utils.log
import org.cqfn.diktat.ruleset.utils.moveChildBefore

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType
import com.pinterest.ktlint.core.ast.ElementType.BLOCK_COMMENT
import com.pinterest.ktlint.core.ast.ElementType.FILE
Expand All @@ -40,16 +39,10 @@ import java.time.LocalDate
* 4) Ensure files with many or zero classes have proper description
*/
@Suppress("ForbiddenComment")
class HeaderCommentRule(private val configRules: List<RulesConfig>) : Rule("header-comment") {
private var isFixMode: Boolean = false
private lateinit var emitWarn: EmitType

override fun visit(node: ASTNode,
autoCorrect: Boolean,
emit: EmitType) {
isFixMode = autoCorrect
emitWarn = emit

class HeaderCommentRule(configRules: List<RulesConfig>) : DiktatRule("header-comment", configRules,
listOf(HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE, HEADER_MISSING_OR_WRONG_COPYRIGHT, HEADER_NOT_BEFORE_PACKAGE,
HEADER_NOT_BEFORE_PACKAGE, HEADER_WRONG_FORMAT, WRONG_COPYRIGHT_YEAR)) {
override fun logic(node: ASTNode) {
if (node.elementType == FILE) {
checkCopyright(node)
if (checkHeaderKdocPosition(node)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ package org.cqfn.diktat.ruleset.rules.chapter2.kdoc
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.EmitType
import org.cqfn.diktat.ruleset.constants.Warnings.COMMENT_WHITE_SPACE
import org.cqfn.diktat.ruleset.constants.Warnings.FIRST_COMMENT_NO_SPACES
import org.cqfn.diktat.ruleset.constants.Warnings.IF_ELSE_COMMENTS
import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_NEWLINES_AROUND_KDOC
import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.utils.*

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.BLOCK
import com.pinterest.ktlint.core.ast.ElementType.BLOCK_COMMENT
import com.pinterest.ktlint.core.ast.ElementType.CLASS
Expand Down Expand Up @@ -50,21 +49,15 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl
* * Leave one single space between the comment on the right side of the code and the code.
* * Comments in if else should be inside code blocks. Exception: General if comment
*/
class CommentsFormatting(private val configRules: List<RulesConfig>) : Rule("kdoc-comments-codeblocks-formatting") {
private var isFixMode: Boolean = false
private lateinit var emitWarn: EmitType

class CommentsFormatting(configRules: List<RulesConfig>) : DiktatRule("kdoc-comments-codeblocks-formatting", configRules,
listOf(COMMENT_WHITE_SPACE, FIRST_COMMENT_NO_SPACES,
IF_ELSE_COMMENTS, WRONG_NEWLINES_AROUND_KDOC)) {
/**
* @param node
* @param autoCorrect
* @param emit
*/
override fun visit(node: ASTNode,
autoCorrect: Boolean,
emit: EmitType) {
isFixMode = autoCorrect
emitWarn = emit

override fun logic(node: ASTNode) {
val configuration = CommentsFormattingConfiguration(
configRules.getRuleConfig(COMMENT_WHITE_SPACE)?.configuration ?: emptyMap())

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ package org.cqfn.diktat.ruleset.rules.chapter2.kdoc

import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.common.config.rules.getCommonConfiguration
import org.cqfn.diktat.ruleset.constants.EmitType
import org.cqfn.diktat.ruleset.constants.Warnings
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_EXTRA_PROPERTY
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NO_CONSTRUCTOR_PROPERTY
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT
import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_CLASS_ELEMENTS
import org.cqfn.diktat.ruleset.constants.Warnings.MISSING_KDOC_TOP_LEVEL
import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.utils.*

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.BLOCK_COMMENT
import com.pinterest.ktlint.core.ast.ElementType.CLASS
import com.pinterest.ktlint.core.ast.ElementType.CLASS_BODY
Expand Down Expand Up @@ -45,23 +44,15 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
* 2) All internal elements in class like class, property or function should be documented with KDoc
* 3) All properties declared in the primary constructor are documented using `@property` tag in class KDoc
*/
class KdocComments(private val configRules: List<RulesConfig>) : Rule("kdoc-comments") {
private var isFixMode: Boolean = false
private lateinit var emitWarn: EmitType

class KdocComments(configRules: List<RulesConfig>) : DiktatRule("kdoc-comments", configRules,
listOf(KDOC_EXTRA_PROPERTY, KDOC_NO_CONSTRUCTOR_PROPERTY,
KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT, MISSING_KDOC_CLASS_ELEMENTS, MISSING_KDOC_TOP_LEVEL)) {
/**
* @param node
* @param autoCorrect
* @param emit
*/
override fun visit(
node: ASTNode,
autoCorrect: Boolean,
emit: EmitType
) {
emitWarn = emit
isFixMode = autoCorrect

override fun logic(node: ASTNode) {
val config = configRules.getCommonConfiguration().value
val filePath = node.getRootNode().getFilePath()
if (!(node.hasTestAnnotation() || isLocatedInTest(filePath.splitPathToDirs(), config.testAnchors))) {
Expand Down
Loading