From bf4f2e5628e2f3f45235d399ace838c985ae0d45 Mon Sep 17 00:00:00 2001 From: aktsay Date: Tue, 8 Sep 2020 11:35:44 +0300 Subject: [PATCH 01/20] Suppress-in-individual-files ### What's done: * First ideas --- .../org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt | 4 ++++ .../kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 7 +++++++ .../diktat/ruleset/chapter3/WhenMustHaveElseWarnTest.kt | 0 3 files changed, 11 insertions(+) create mode 100644 diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/WhenMustHaveElseWarnTest.kt diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt index fd255a8fa0..d9dd7042ec 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt @@ -11,6 +11,7 @@ import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.ruleset.constants.Warnings.COMMENTED_OUT_CODE import org.cqfn.diktat.ruleset.rules.getDiktatConfigRules import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType +import org.cqfn.diktat.ruleset.utils.hasSuppress import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.TokenType import org.jetbrains.kotlin.lexer.KtTokens @@ -42,6 +43,9 @@ class CommentsRule : Rule("comments") { params: KtLint.Params, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit ) { + if (node.hasSuppress(COMMENTED_OUT_CODE.name)) + return + configRules = params.getDiktatConfigRules() emitWarn = emit isFixMode = autoCorrect diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index 12312a5013..c39df3a897 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -1,6 +1,8 @@ package org.cqfn.diktat.ruleset.utils import com.pinterest.ktlint.core.ast.ElementType +import com.pinterest.ktlint.core.ast.ElementType.ANNOTATION +import com.pinterest.ktlint.core.ast.ElementType.ANNOTATION_ENTRY import com.pinterest.ktlint.core.ast.ElementType.CONST_KEYWORD import com.pinterest.ktlint.core.ast.ElementType.FILE import com.pinterest.ktlint.core.ast.ElementType.INTERNAL_KEYWORD @@ -291,6 +293,11 @@ fun ASTNode?.isAccessibleOutside(): Boolean = true } +fun ASTNode.hasSuppress(warningName: String): Boolean = parent(ANNOTATION) + .let { node -> + node?.getAllChildrenWithType(ANNOTATION_ENTRY)!!.any { it.text.contains("Suppress") + && it.text.contains(warningName) } + } /** * removing all newlines in WHITE_SPACE node and replacing it to a one newline saving the initial indenting format */ diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/WhenMustHaveElseWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/WhenMustHaveElseWarnTest.kt new file mode 100644 index 0000000000..e69de29bb2 From 2412b4be80a107510b7f8dfed3aed2d4b02b6b12 Mon Sep 17 00:00:00 2001 From: aktsay Date: Tue, 8 Sep 2020 16:48:53 +0300 Subject: [PATCH 02/20] feature/suppress-in-individual-files ### What's done: * Warn method remade * Added suppress check * Added test --- .../cqfn/diktat/ruleset/constants/Warnings.kt | 13 ++++++++-- .../ruleset/rules/BlockStructureBraces.kt | 8 +++--- .../rules/BracesInConditionalsAndLoopsRule.kt | 8 +++--- .../rules/ClassLikeStructuresOrderRule.kt | 4 +-- .../ruleset/rules/ConsecutiveSpacesRule.kt | 2 +- .../cqfn/diktat/ruleset/rules/EmptyBlock.kt | 6 ++--- .../diktat/ruleset/rules/EnumsSeparated.kt | 8 +++--- .../cqfn/diktat/ruleset/rules/FileNaming.kt | 4 +-- .../diktat/ruleset/rules/IdentifierNaming.kt | 26 +++++++++---------- .../cqfn/diktat/ruleset/rules/LineLength.kt | 2 +- .../diktat/ruleset/rules/PackageNaming.kt | 12 ++++----- .../ruleset/rules/SingleLineStatementsRule.kt | 2 +- .../ruleset/rules/StringConcatenationRule.kt | 2 +- .../diktat/ruleset/rules/WhiteSpaceRule.kt | 12 ++++----- .../ruleset/rules/comments/CommentsRule.kt | 2 +- .../rules/comments/HeaderCommentRule.kt | 10 +++---- .../ruleset/rules/files/BlankLinesRule.kt | 4 +-- .../diktat/ruleset/rules/files/FileSize.kt | 2 +- .../ruleset/rules/files/FileStructureRule.kt | 10 +++---- .../ruleset/rules/files/IndentationRule.kt | 6 ++--- .../ruleset/rules/files/NewlinesRule.kt | 16 ++++++------ .../diktat/ruleset/rules/kdoc/KdocComments.kt | 2 +- .../ruleset/rules/kdoc/KdocFormatting.kt | 18 ++++++------- .../diktat/ruleset/rules/kdoc/KdocMethods.kt | 12 ++++----- .../cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 13 +++++++--- .../cqfn/diktat/ruleset/utils/SuppressTest.kt | 25 ++++++++++++++++++ 26 files changed, 134 insertions(+), 95 deletions(-) create mode 100644 diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt index d4916b19be..d357de78e4 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt @@ -3,6 +3,8 @@ package org.cqfn.diktat.ruleset.constants import org.cqfn.diktat.common.config.rules.Rule import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.common.config.rules.isRuleEnabled +import org.cqfn.diktat.ruleset.utils.hasSuppress +import org.jetbrains.kotlin.com.intellij.lang.ASTNode @Suppress("ForbiddenComment", "MagicNumber") enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: String) : Rule { @@ -90,8 +92,11 @@ enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: S isFixMode: Boolean, freeText: String, offset: Int, + node: ASTNode?, autoFix: () -> Unit) { - warn(configRules, emit, this.canBeAutoCorrected, freeText, offset) + if (node != null && node.hasSuppress(name)) + return + warn(configRules, emit, this.canBeAutoCorrected, freeText, offset, node) fix(configRules, autoFix, isFixMode) } @@ -99,7 +104,11 @@ enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: S emit: ((offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit), autoCorrected: Boolean, freeText: String, - offset: Int) { + offset: Int, + node: ASTNode?) { + if (node != null && node.hasSuppress(name)) + return + if (configs.isRuleEnabled(this)) { emit(offset, "${this.warnText()} $freeText", diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BlockStructureBraces.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BlockStructureBraces.kt index 966efdc00a..e0de97f24d 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BlockStructureBraces.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BlockStructureBraces.kt @@ -155,7 +155,7 @@ class BlockStructureBraces : Rule("block-structure") { val braceSpace = nodeBefore?.treePrev if (braceSpace == null || checkBraceNode(braceSpace, true)) BRACES_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "incorrect newline before opening brace", - (braceSpace ?: node).startOffset) { + (braceSpace ?: node).startOffset, node) { if (braceSpace == null || braceSpace.elementType != WHITE_SPACE) { node.addChild(PsiWhiteSpaceImpl(" "), nodeBefore) } else { @@ -170,7 +170,7 @@ class BlockStructureBraces : Rule("block-structure") { node.findChildByType(beforeType) else node)?.findLBrace()?.treeNext ?: return if (checkBraceNode(newNode)) { BRACES_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "incorrect same line after opening brace", - newNode.startOffset) { + newNode.startOffset, node) { if (newNode.elementType != WHITE_SPACE) { node.addChild(PsiWhiteSpaceImpl("\n"), newNode) } else { @@ -184,7 +184,7 @@ class BlockStructureBraces : Rule("block-structure") { allMiddleSpace.forEach { if (checkBraceNode(it, true)) { BRACES_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "incorrect new line after closing brace", - it.startOffset) { + it.startOffset, node) { if (it.elementType != WHITE_SPACE) { node.addChild(PsiWhiteSpaceImpl(" "), node.findChildByType(keyword)) } else { @@ -200,7 +200,7 @@ class BlockStructureBraces : Rule("block-structure") { val space = node.findChildByType(RBRACE)!!.treePrev if (checkBraceNode(space)) BRACES_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "no newline before closing brace", - (space.treeNext ?: node.findChildByType(RBRACE))!!.startOffset) { + (space.treeNext ?: node.findChildByType(RBRACE))!!.startOffset, node) { if (space.elementType != WHITE_SPACE) { node.addChild(PsiWhiteSpaceImpl("\n"), node.findChildByType(RBRACE)) } else { diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BracesInConditionalsAndLoopsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BracesInConditionalsAndLoopsRule.kt index 4eb224c3de..2af9c9891a 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BracesInConditionalsAndLoopsRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BracesInConditionalsAndLoopsRule.kt @@ -74,7 +74,7 @@ class BracesInConditionalsAndLoopsRule : Rule("braces-rule") { if (thenNode?.elementType != BLOCK) { NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnAndFix(configRules, emitWarn, isFixMode, "IF", - (thenNode?.prevSibling { it.elementType == IF_KEYWORD } ?: node).startOffset) { + (thenNode?.prevSibling { it.elementType == IF_KEYWORD } ?: node).startOffset, node) { if (thenNode != null) { ifPsi.then!!.replaceWithBlock(indent) if (elseNode != null) { @@ -89,7 +89,7 @@ class BracesInConditionalsAndLoopsRule : Rule("braces-rule") { if (hasElseBranch && elseNode?.elementType != IF && elseNode?.elementType != BLOCK) { NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnAndFix(configRules, emitWarn, isFixMode, "ELSE", - (elseNode?.treeParent?.prevSibling { it.elementType == ELSE_KEYWORD } ?: node).startOffset) { + (elseNode?.treeParent?.prevSibling { it.elementType == ELSE_KEYWORD } ?: node).startOffset, node) { if (elseNode != null) { ifPsi.`else`!!.replaceWithBlock(indent) } else { @@ -104,7 +104,7 @@ class BracesInConditionalsAndLoopsRule : Rule("braces-rule") { val loopBody = (node.psi as KtLoopExpression).body val loopBodyNode = loopBody?.node if (loopBodyNode == null || loopBodyNode.elementType != BLOCK) { - NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnAndFix(configRules, emitWarn, isFixMode, node.elementType.toString(), node.startOffset) { + NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnAndFix(configRules, emitWarn, isFixMode, node.elementType.toString(), node.startOffset, node) { // fixme proper way to calculate indent? or get step size (instead of hardcoded 4) val indent = node.prevSibling { it.elementType == WHITE_SPACE }!!.text.lines().last().count { it == ' ' } if (loopBody != null) { @@ -127,7 +127,7 @@ class BracesInConditionalsAndLoopsRule : Rule("braces-rule") { .map { it.expression as KtBlockExpression } .filter { it.statements.size == 1 } .forEach { - NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnAndFix(configRules, emitWarn, isFixMode, "WHEN", it.node.startOffset) { + NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnAndFix(configRules, emitWarn, isFixMode, "WHEN", it.node.startOffset, node) { it.astReplace(it.firstStatement!!.node.psi) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ClassLikeStructuresOrderRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ClassLikeStructuresOrderRule.kt index cb9a1d6af3..e7d20e582a 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ClassLikeStructuresOrderRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ClassLikeStructuresOrderRule.kt @@ -85,7 +85,7 @@ class ClassLikeStructuresOrderRule : Rule("class-like-structures") { unusedClasses) blocks.allBlockFlattened().reversed().handleIncorrectOrder(blocks::getSiblingBlocks) { astNode, beforeThisNode -> - WRONG_ORDER_IN_CLASS_LIKE_STRUCTURES.warnAndFix(configRules, emitWarn, isFixMode, astNode.elementType.toString() + ": " + astNode.text, astNode.startOffset) { + WRONG_ORDER_IN_CLASS_LIKE_STRUCTURES.warnAndFix(configRules, emitWarn, isFixMode, astNode.elementType.toString() + ": " + astNode.text, astNode.startOffset, astNode) { val replacement = node.moveChildBefore(astNode, beforeThisNode, true) replacement.oldNodes.forEachIndexed { idx, oldNode -> blocks.allBlocks().find { oldNode in it }?.apply { @@ -104,7 +104,7 @@ class ClassLikeStructuresOrderRule : Rule("class-like-structures") { val whiteSpaceBefore = previousProperty.nextSibling { it.elementType == WHITE_SPACE }!! val nRequiredNewLines = if (commentOnThis == null) 1 else 2 if (whiteSpaceBefore.text.count { it == '\n' } != nRequiredNewLines) - BLANK_LINE_BETWEEN_PROPERTIES.warnAndFix(configRules, emitWarn, isFixMode, node.getIdentifierName()!!.text, node.startOffset) { + BLANK_LINE_BETWEEN_PROPERTIES.warnAndFix(configRules, emitWarn, isFixMode, node.getIdentifierName()!!.text, node.startOffset, node) { whiteSpaceBefore.leaveExactlyNumNewLines(nRequiredNewLines) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ConsecutiveSpacesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ConsecutiveSpacesRule.kt index ab21108858..5dceda5b5b 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ConsecutiveSpacesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ConsecutiveSpacesRule.kt @@ -76,7 +76,7 @@ class ConsecutiveSpacesRule : Rule("too-many-spaces") { if (spaces > configuration.numberOfSpaces && !node.isWhiteSpaceWithNewline() && !node.hasEolComment()) { TOO_MANY_CONSECUTIVE_SPACES.warnAndFix(configRules, emitWarn, isFixMode, - "found: $spaces. need to be: ${configuration.numberOfSpaces}", node.startOffset) { + "found: $spaces. need to be: ${configuration.numberOfSpaces}", node.startOffset, node) { node.squeezeSpaces() } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EmptyBlock.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EmptyBlock.kt index b9186e34a8..87b0986286 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EmptyBlock.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EmptyBlock.kt @@ -48,12 +48,12 @@ class EmptyBlock : Rule("empty-block-structure") { if (node.isBlockEmpty()) { if (!configuration.emptyBlockExist) { EMPTY_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "empty blocks are forbidden unless it is function with override keyword", - node.startOffset) {} + node.startOffset, node) {} } else { val space = node.findChildByType(RBRACE)!!.treePrev if (configuration.emptyBlockNewline && !space.text.contains("\n")) { EMPTY_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "different style for empty block", - node.startOffset) { + node.startOffset, node) { if (space.elementType == WHITE_SPACE) (space.treeNext as LeafPsiElement).replaceWithText("\n") else @@ -61,7 +61,7 @@ class EmptyBlock : Rule("empty-block-structure") { } } else if (!configuration.emptyBlockNewline && space.text.contains("\n")) { EMPTY_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "different style for empty block", - node.startOffset) { + node.startOffset, node) { node.removeChild(space) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EnumsSeparated.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EnumsSeparated.kt index a3bd13c065..b57aace6de 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EnumsSeparated.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EnumsSeparated.kt @@ -55,7 +55,7 @@ class EnumsSeparated : Rule("enum-separated") { enumEntries.forEach { if (!it.treeNext.isWhiteSpaceWithNewline()) ENUMS_SEPARATED.warnAndFix(configRules, emitWarn, isFixMode, "enum entries must end with a line break", - it.startOffset) { + it.startOffset, node) { it.appendNewlineMergingWhiteSpace(it.treeNext, it.treeNext) } } @@ -76,19 +76,19 @@ class EnumsSeparated : Rule("enum-separated") { private fun checkLastEnum(node: ASTNode) { if (!node.hasChildOfType(SEMICOLON)) { ENUMS_SEPARATED.warnAndFix(configRules, emitWarn, isFixMode, "enums must end with semicolon", - node.startOffset) { + node.startOffset, node) { node.addChild(LeafPsiElement(SEMICOLON, ";"), null) node.addChild(PsiWhiteSpaceImpl("\n"), node.findChildByType(SEMICOLON)!!) } } else if (!node.findChildByType(SEMICOLON)!!.treePrev.isWhiteSpaceWithNewline()) { ENUMS_SEPARATED.warnAndFix(configRules, emitWarn, isFixMode, "semicolon must be on a new line", - node.startOffset) { + node.startOffset, node) { node.appendNewlineMergingWhiteSpace(node.findChildByType(SEMICOLON)!!, node.findChildByType(SEMICOLON)!!) } } if (!node.hasChildOfType(COMMA)) { ENUMS_SEPARATED.warnAndFix(configRules, emitWarn, isFixMode, "last enum entry must end with a comma", - node.startOffset) { + node.startOffset, node) { node.addChild(LeafPsiElement(COMMA, ","), node.findChildByType(SEMICOLON)!!.treePrev) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/FileNaming.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/FileNaming.kt index 6a9de3bca2..3dd258db67 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/FileNaming.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/FileNaming.kt @@ -54,7 +54,7 @@ class FileNaming : Rule("file-naming") { if (fileName != null) { val (name, extension) = getFileParts() if (!name.isPascalCase() || !VALID_EXTENSIONS.contains(extension)) { - FILE_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, "$name$extension", 0) { + FILE_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, "$name$extension", 0, null) { // FixMe: we can add an autocorrect here in future, but is there any purpose to change file or class name? } } @@ -68,7 +68,7 @@ class FileNaming : Rule("file-naming") { if (classes.size == 1) { val className = classes[0].getFirstChildWithType(IDENTIFIER)!!.text if (className != fileNameWithoutSuffix) { - FILE_NAME_MATCH_CLASS.warnAndFix(configRules, emitWarn, isFixMode, "$fileNameWithoutSuffix$fileNameSuffix vs $className", 0) { + FILE_NAME_MATCH_CLASS.warnAndFix(configRules, emitWarn, isFixMode, "$fileNameWithoutSuffix$fileNameSuffix vs $className", 0, fileLevelNode) { // FixMe: we can add an autocorrect here in future, but is there any purpose to change file name? } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt index f8f6803e8a..e93e9c076e 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt @@ -107,7 +107,7 @@ class IdentifierNaming : Rule("identifier-naming") { if (identifierText?.startsWith('`') == true && identifierText.endsWith('`')) { // the only exception is test method with @Test annotation if (!(node.elementType == ElementType.FUN && node.hasTestAnnotation())) { - BACKTICKS_PROHIBITED.warn(configRules, emitWarn, isFixMode, identifierText, identifier.startOffset) + BACKTICKS_PROHIBITED.warn(configRules, emitWarn, isFixMode, identifierText, identifier.startOffset, node) } return true } @@ -126,19 +126,19 @@ class IdentifierNaming : Rule("identifier-naming") { // variable should not contain only one letter in it's name. This is a bad example: b512 // but no need to raise a warning here if length of a variable. In this case we will raise IDENTIFIER_LENGTH if (variableName.text.containsOneLetterOrZero() && variableName.text.length > 1) { - VARIABLE_NAME_INCORRECT.warn(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset) + VARIABLE_NAME_INCORRECT.warn(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset, node) } // check for constant variables - check for val from companion object or on global file level // it should be in UPPER_CASE, no need to raise this warning if it is one-letter variable if (node.isConstant()) { if (!variableName.text.isUpperSnakeCase() && variableName.text.length > 1) { - CONSTANT_UPPERCASE.warnAndFix(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset) { + CONSTANT_UPPERCASE.warnAndFix(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset,node) { (variableName as LeafPsiElement).replaceWithText(variableName.text.toUpperSnakeCase()) } } } else if (!variableName.text.isLowerCamelCase()) { // variable name should be in camel case. The only exception is a list of industry standard variables like i, j, k. - VARIABLE_NAME_INCORRECT_FORMAT.warnAndFix(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset) { + VARIABLE_NAME_INCORRECT_FORMAT.warnAndFix(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset, node) { // FixMe: cover fixes with tests (variableName as LeafPsiElement).replaceWithText(variableName.text.toLowerCamelCase()) } @@ -152,7 +152,7 @@ class IdentifierNaming : Rule("identifier-naming") { .forEach { variableName -> // generally, variables with prefixes are not allowed (like mVariable, xCode, iValue) if (variableName.text.hasPrefix()) { - VARIABLE_HAS_PREFIX.warnAndFix(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset) { + VARIABLE_HAS_PREFIX.warnAndFix(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset, node) { (variableName as LeafPsiElement).replaceWithText(variableName.text.removePrefix()) } } @@ -189,14 +189,14 @@ class IdentifierNaming : Rule("identifier-naming") { private fun checkCLassNamings(node: ASTNode): List { val genericType: ASTNode? = node.getTypeParameterList() if (genericType != null && !validGenericTypeName(genericType.text)) { - GENERIC_NAME.warnAndFix(configRules, emitWarn, isFixMode, genericType.text, genericType.startOffset) { + GENERIC_NAME.warnAndFix(configRules, emitWarn, isFixMode, genericType.text, genericType.startOffset, node) { // FixMe: should fix generic name here } } val className: ASTNode? = node.getIdentifierName() if (!(className!!.text.isPascalCase())) { - CLASS_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, className.text, className.startOffset) { + CLASS_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, className.text, className.startOffset, node) { (className as LeafPsiElement).replaceWithText(className.text.toPascalCase()) } } @@ -221,7 +221,7 @@ class IdentifierNaming : Rule("identifier-naming") { ?.text if (superClassName != null && hasExceptionSuffix(superClassName) && !hasExceptionSuffix(classNameNode!!.text)) { - EXCEPTION_SUFFIX.warnAndFix(configRules, emitWarn, isFixMode, classNameNode.text, classNameNode.startOffset) { + EXCEPTION_SUFFIX.warnAndFix(configRules, emitWarn, isFixMode, classNameNode.text, classNameNode.startOffset, node) { // FixMe: need to add tests for this (classNameNode as LeafPsiElement).replaceWithText(classNameNode.text + "Exception") } @@ -236,7 +236,7 @@ class IdentifierNaming : Rule("identifier-naming") { // if this object is companion object or anonymous object - it does not have any name val objectName: ASTNode = node.getIdentifierName() ?: return listOf() if (!objectName.text.isPascalCase()) { - OBJECT_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, objectName.text, objectName.startOffset) { + OBJECT_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, objectName.text, objectName.startOffset, node) { (objectName as LeafPsiElement).replaceWithText(objectName.text.toPascalCase()) } } @@ -252,7 +252,7 @@ class IdentifierNaming : Rule("identifier-naming") { val enumValues: List = node.getChildren(null).filter { it.elementType == ElementType.IDENTIFIER } enumValues.forEach { value -> if (!value.text.isUpperSnakeCase()) { - ENUM_VALUE.warnAndFix(configRules, emitWarn, isFixMode, value.text, value.startOffset) { + ENUM_VALUE.warnAndFix(configRules, emitWarn, isFixMode, value.text, value.startOffset, node) { // FixMe: add tests for this (value as LeafPsiElement).replaceWithText(value.text.toUpperSnakeCase()) } @@ -274,7 +274,7 @@ class IdentifierNaming : Rule("identifier-naming") { // basic check for camel case if (!functionName.text.isLowerCamelCase()) { - FUNCTION_NAME_INCORRECT_CASE.warnAndFix(configRules, emitWarn, isFixMode, functionName.text, functionName.startOffset) { + FUNCTION_NAME_INCORRECT_CASE.warnAndFix(configRules, emitWarn, isFixMode, functionName.text, functionName.startOffset, node) { // FixMe: add tests for this (functionName as LeafPsiElement).replaceWithText(functionName.text.toLowerCamelCase()) } @@ -286,7 +286,7 @@ class IdentifierNaming : Rule("identifier-naming") { // if function has Boolean return type in 99% of cases it is much better to name it with isXXX or hasXXX prefix if (functionReturnType != null && functionReturnType == PrimitiveType.BOOLEAN.typeName.asString()) { if (BOOLEAN_METHOD_PREFIXES.none { functionName.text.startsWith(it) }) { - FUNCTION_BOOLEAN_PREFIX.warnAndFix(configRules, emitWarn, isFixMode, functionName.text, functionName.startOffset) { + FUNCTION_BOOLEAN_PREFIX.warnAndFix(configRules, emitWarn, isFixMode, functionName.text, functionName.startOffset, node) { // FixMe: add agressive autofix for this } } @@ -319,7 +319,7 @@ class IdentifierNaming : Rule("identifier-naming") { ONE_CHAR_IDENTIFIERS.contains(it.text) && isVariable || validCatchIdentifier(it) )) { - IDENTIFIER_LENGTH.warn(configRules, emitWarn, isFixMode, it.text, it.startOffset) + IDENTIFIER_LENGTH.warn(configRules, emitWarn, isFixMode, it.text, it.startOffset, it) } } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LineLength.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LineLength.kt index 82593ba8c4..0d6ca734fb 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LineLength.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LineLength.kt @@ -54,7 +54,7 @@ class LineLength : Rule("line-length") { if ((newNode.elementType != KDOC_TEXT && newNode.elementType != KDOC_MARKDOWN_INLINE_LINK) || !isKDocValid(newNode)) { LONG_LINE.warnAndFix(configRules, emitWarn, isFixMode, "max line length ${configuration.lineLength}, but was ${it.length}", - offset + node.startOffset) {} + offset + node.startOffset, node) {} } } offset += it.length + 1 diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/PackageNaming.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/PackageNaming.kt index 5d8bb82643..ecaa79d499 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/PackageNaming.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/PackageNaming.kt @@ -90,7 +90,7 @@ class PackageNaming : Rule("package-naming") { * checking and fixing the case when package directive is missing in the file */ private fun checkMissingPackageName(packageDirectiveNode: ASTNode, realPackageName: List, fileName: String) { - PACKAGE_NAME_MISSING.warnAndFix(configRules, emitWarn, isFixMode, fileName, packageDirectiveNode.startOffset) { + PACKAGE_NAME_MISSING.warnAndFix(configRules, emitWarn, isFixMode, fileName, packageDirectiveNode.startOffset, packageDirectiveNode) { if (realPackageName.isNotEmpty()) { packageDirectiveNode.addChild(LeafPsiElement(PACKAGE_KEYWORD, PACKAGE_KEYWORD.toString()), null) packageDirectiveNode.addChild(PsiWhiteSpaceImpl(" "), null) @@ -135,14 +135,14 @@ class PackageNaming : Rule("package-naming") { wordsInPackageName .filter { word -> word.text.hasUppercaseLetter() } .forEach { - PACKAGE_NAME_INCORRECT_CASE.warnAndFix(configRules, emitWarn, isFixMode, it.text, it.startOffset) { + PACKAGE_NAME_INCORRECT_CASE.warnAndFix(configRules, emitWarn, isFixMode, it.text, it.startOffset, it) { it.toLower() } } // package name should start from a company's domain name if (!isDomainMatches(wordsInPackageName)) { - PACKAGE_NAME_INCORRECT_PREFIX.warnAndFix(configRules, emitWarn, isFixMode, domainName, wordsInPackageName[0].startOffset) { + PACKAGE_NAME_INCORRECT_PREFIX.warnAndFix(configRules, emitWarn, isFixMode, domainName, wordsInPackageName[0].startOffset, wordsInPackageName[0]) { val parentNodeToInsert = wordsInPackageName[0].parent(DOT_QUALIFIED_EXPRESSION)!! createAndInsertPackageName(parentNodeToInsert, wordsInPackageName[0].treeParent, domainName.split(PACKAGE_SEPARATOR)) } @@ -151,7 +151,7 @@ class PackageNaming : Rule("package-naming") { // all words should contain only ASCII letters or digits wordsInPackageName .filter { word -> !correctSymbolsAreUsed(word.text) } - .forEach { PACKAGE_NAME_INCORRECT_SYMBOLS.warn(configRules, emitWarn, isFixMode, it.text, it.startOffset) } + .forEach { PACKAGE_NAME_INCORRECT_SYMBOLS.warn(configRules, emitWarn, isFixMode, it.text, it.startOffset, it) } // all words should contain only ASCII letters or digits wordsInPackageName.forEach { correctPackageWordSeparatorsUsed(it) } @@ -172,7 +172,7 @@ class PackageNaming : Rule("package-naming") { */ private fun correctPackageWordSeparatorsUsed(word: ASTNode) { if (word.text.contains("_") && !exceptionForUnderscore(word.text)) { - INCORRECT_PACKAGE_SEPARATOR.warnAndFix(configRules, emitWarn, isFixMode, word.text, word.startOffset) { + INCORRECT_PACKAGE_SEPARATOR.warnAndFix(configRules, emitWarn, isFixMode, word.text, word.startOffset, word) { (word as LeafPsiElement).replaceWithText(word.text.replace("_", "")) } } @@ -239,7 +239,7 @@ class PackageNaming : Rule("package-naming") { */ private fun checkFilePathMatchesWithPackageName(packageNameParts: List, realName: List) { if (realName.isNotEmpty() && packageNameParts.map { node -> node.text } != realName) { - PACKAGE_NAME_INCORRECT_PATH.warnAndFix(configRules, emitWarn, isFixMode, realName.joinToString(PACKAGE_SEPARATOR), packageNameParts[0].startOffset) { + PACKAGE_NAME_INCORRECT_PATH.warnAndFix(configRules, emitWarn, isFixMode, realName.joinToString(PACKAGE_SEPARATOR), packageNameParts[0].startOffset, packageNameParts[0]) { // need to get first top-level DOT-QUALIFIED-EXPRESSION // -- PACKAGE_DIRECTIVE // -- DOT_QUALIFIED_EXPRESSION diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SingleLineStatementsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SingleLineStatementsRule.kt index 011500f738..3957d9dbc2 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SingleLineStatementsRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SingleLineStatementsRule.kt @@ -44,7 +44,7 @@ class SingleLineStatementsRule : Rule("statement") { node.getChildren(semicolonToken).forEach { if (!it.isFollowedByNewline()) { MORE_THAN_ONE_STATEMENT_PER_LINE.warnAndFix(configRules, emitWarn, isFixMode, it.extractLineOfText(), - it.startOffset) { + it.startOffset, it) { if (it.treeParent.elementType == ENUM_ENTRY) { node.treeParent.addChild(PsiWhiteSpaceImpl("\n"), node.treeNext) } else { diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/StringConcatenationRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/StringConcatenationRule.kt index a472ec610e..11188fe627 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/StringConcatenationRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/StringConcatenationRule.kt @@ -54,7 +54,7 @@ class StringConcatenationRule : Rule("string-concatenation") { assert(node.elementType == BINARY_EXPRESSION) val firstChild = node.firstChildNode return if (isPlusBinaryExpression(node) && firstChild.elementType == STRING_TEMPLATE) { - STRING_CONCATENATION.warn(configRules, emitWarn, this.isFixMode, parentNode.text, firstChild.startOffset) + STRING_CONCATENATION.warn(configRules, emitWarn, this.isFixMode, parentNode.text, firstChild.startOffset, firstChild) true } else { false diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/WhiteSpaceRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/WhiteSpaceRule.kt index 9fb11f9401..9a3b2fa334 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/WhiteSpaceRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/WhiteSpaceRule.kt @@ -133,7 +133,7 @@ class WhiteSpaceRule : Rule("horizontal-whitespace") { if (node.treeNext.numWhiteSpaces()?.let { it > 0 } == true) { // there is either whitespace or newline after constructor keyword WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, "keyword '${node.text}' should not be separated from " + - "'(' with a whitespace", node.startOffset) { + "'(' with a whitespace", node.startOffset, node) { node.treeParent.removeChild(node.treeNext) } } @@ -148,7 +148,7 @@ class WhiteSpaceRule : Rule("horizontal-whitespace") { return } WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, "keyword '${node.text}' should be separated from " + - "'${nextCodeLeaf.text}' with a whitespace", nextCodeLeaf.startOffset) { + "'${nextCodeLeaf.text}' with a whitespace", nextCodeLeaf.startOffset, node) { node.leaveSingleWhiteSpace() } } @@ -176,13 +176,13 @@ class WhiteSpaceRule : Rule("horizontal-whitespace") { if (isFromLambdaAsArgument) { if (numWhiteSpace != 0) { WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, "there should be no whitespace before '{' of lambda" + - " inside argument list", node.startOffset) { + " inside argument list", node.startOffset, node) { whitespaceOrPrevNode.treeParent.removeChild(whitespaceOrPrevNode) } } } else if (prevNode.elementType !in keywordsWithSpaceAfter) { if (numWhiteSpace != 1) { - WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, "there should be a whitespace before '{'", node.startOffset) { + WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, "there should be a whitespace before '{'", node.startOffset, node) { prevNode.leaveSingleWhiteSpace() } } @@ -223,7 +223,7 @@ class WhiteSpaceRule : Rule("horizontal-whitespace") { getDescription(requiredSpacesBefore != null, requiredSpacesAfter != null, requiredSpacesBefore, requiredSpacesAfter) + ", but has" + getDescription(isErrorBefore, isErrorAfter, spacesBefore, spacesAfter) - WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, freeText, node.startOffset) { + WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, freeText, node.startOffset, node) { node.fixSpaceAround(requiredSpacesBefore, requiredSpacesAfter) } } @@ -234,7 +234,7 @@ class WhiteSpaceRule : Rule("horizontal-whitespace") { // the second condition corresponds to the last line of file val isEol = node.textContains('\n') || node.psi.parentsWithSelf.all { it.nextSibling == null } if (hasSpaces && isEol) { - WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, "there should be no spaces in the end of line", node.startOffset) { + WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, "there should be no spaces in the end of line", node.startOffset, node) { (node as LeafElement).replaceWithText(node.text.trimStart(' ')) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt index d9dd7042ec..a24de1bd02 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt @@ -88,7 +88,7 @@ class CommentsRule : Rule("comments") { .filter { (_, parsedNode) -> parsedNode.findAllNodesWithSpecificType(TokenType.ERROR_ELEMENT).isEmpty() }.forEach { (offset, parsedNode) -> - COMMENTED_OUT_CODE.warn(configRules, emitWarn, isFixMode, parsedNode.text.substringBefore("\n").trim(), offset) + COMMENTED_OUT_CODE.warn(configRules, emitWarn, isFixMode, parsedNode.text.substringBefore("\n").trim(), offset, node) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/HeaderCommentRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/HeaderCommentRule.kt index ae3ccc2390..6f4073e05b 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/HeaderCommentRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/HeaderCommentRule.kt @@ -77,7 +77,7 @@ class HeaderCommentRule : Rule("header-comment") { } if (isWrongCopyright || isMissingCopyright || isCopyrightInsideKdoc) { - HEADER_MISSING_OR_WRONG_COPYRIGHT.warnAndFix(configRules, emitWarn, isFixMode, fileName, node.startOffset) { + HEADER_MISSING_OR_WRONG_COPYRIGHT.warnAndFix(configRules, emitWarn, isFixMode, fileName, node.startOffset, node) { if (headerComment != null) { node.removeChild(headerComment) } @@ -107,7 +107,7 @@ class HeaderCommentRule : Rule("header-comment") { val hasOrphanedKdocAfterImports = firstKdoc != null && firstKdoc.treeParent.elementType == FILE if (configRules.isRuleEnabled(HEADER_NOT_BEFORE_PACKAGE) && node.findChildBefore(PACKAGE_DIRECTIVE, KDOC) == null && hasOrphanedKdocAfterImports) { - HEADER_NOT_BEFORE_PACKAGE.warnAndFix(configRules, emitWarn, isFixMode, fileName, firstKdoc!!.startOffset) { + HEADER_NOT_BEFORE_PACKAGE.warnAndFix(configRules, emitWarn, isFixMode, fileName, firstKdoc!!.startOffset, firstKdoc) { node.moveChildBefore(firstKdoc, node.getFirstChildWithType(PACKAGE_DIRECTIVE), true) // ensure there is no empty line between copyright and header kdoc node.findChildBefore(PACKAGE_DIRECTIVE, BLOCK_COMMENT)?.apply { @@ -128,7 +128,7 @@ class HeaderCommentRule : Rule("header-comment") { if (headerKdoc == null) { val nDeclaredClasses = node.getAllChildrenWithType(ElementType.CLASS).size if (nDeclaredClasses == 0 || nDeclaredClasses > 1) { - HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE.warn(configRules, emitWarn, isFixMode, fileName, node.startOffset) + HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE.warn(configRules, emitWarn, isFixMode, fileName, node.startOffset, node) } } else { // fixme we should also check date of creation, but it can be in different formats @@ -136,13 +136,13 @@ class HeaderCommentRule : Rule("header-comment") { .filter { it.contains("@author") } .forEach { HEADER_CONTAINS_DATE_OR_AUTHOR.warn(configRules, emitWarn, isFixMode, - it.trim(), headerKdoc.startOffset) + it.trim(), headerKdoc.startOffset, headerKdoc) } if (headerKdoc.treeNext != null && headerKdoc.treeNext.elementType == WHITE_SPACE && headerKdoc.treeNext.text.count { it == '\n' } != 2) { HEADER_WRONG_FORMAT.warnAndFix(configRules, emitWarn, isFixMode, - "header KDoc should have a new line after", headerKdoc.startOffset) { + "header KDoc should have a new line after", headerKdoc.startOffset, headerKdoc) { node.replaceChild(headerKdoc.treeNext, PsiWhiteSpaceImpl("\n\n")) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/BlankLinesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/BlankLinesRule.kt index 91b30a125b..e3b4795142 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/BlankLinesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/BlankLinesRule.kt @@ -50,7 +50,7 @@ class BlankLinesRule : Rule("blank-lines") { // if both are present, this is not beginning or end // if both are null, then this block is empty and is handled in another rule val freeText = "do not put newlines ${if (node.treePrev.elementType == LBRACE) "in the beginning" else "at the end"} of code blocks" - TOO_MANY_BLANK_LINES.warnAndFix(configRules, emitWarn, isFixMode, freeText, node.startOffset) { + TOO_MANY_BLANK_LINES.warnAndFix(configRules, emitWarn, isFixMode, freeText, node.startOffset, node) { node.leaveOnlyOneNewLine() } } @@ -58,7 +58,7 @@ class BlankLinesRule : Rule("blank-lines") { } private fun handleTooManyBlankLines(node: ASTNode) { - TOO_MANY_BLANK_LINES.warnAndFix(configRules, emitWarn, isFixMode, "do not use more than two consecutive blank lines", node.startOffset) { + TOO_MANY_BLANK_LINES.warnAndFix(configRules, emitWarn, isFixMode, "do not use more than two consecutive blank lines", node.startOffset, node) { node.leaveExactlyNumNewLines(2) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileSize.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileSize.kt index 33053a3076..eb8d3e7a33 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileSize.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileSize.kt @@ -65,7 +65,7 @@ class FileSize : Rule("file-size") { private fun checkFileSize(node: ASTNode, maxSize: Long) { val size = node.text.split("\n").size if (size > maxSize) { - FILE_IS_TOO_LONG.warn(configRules, emitWarn, isFixMode, "$size", node.startOffset) + FILE_IS_TOO_LONG.warn(configRules, emitWarn, isFixMode, "$size", node.startOffset, node) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt index 4385ff4f51..289f4e6381 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt @@ -65,7 +65,7 @@ class FileStructureRule : Rule("file-structure") { val codeTokens = TokenSet.andNot(TokenSet.ANY, TokenSet.create(WHITE_SPACE, KDOC, BLOCK_COMMENT, EOL_COMMENT, PACKAGE_DIRECTIVE, IMPORT_LIST)) val hasCode = node.getChildren(codeTokens).isNotEmpty() if (!hasCode) { - FILE_CONTAINS_ONLY_COMMENTS.warn(configRules, emitWarn, isFixMode, fileName, node.startOffset) + FILE_CONTAINS_ONLY_COMMENTS.warn(configRules, emitWarn, isFixMode, fileName, node.startOffset, node) } return hasCode } @@ -84,7 +84,7 @@ class FileStructureRule : Rule("file-structure") { listOfNotNull(copyrightComment, headerKdoc, fileAnnotations).handleIncorrectOrder({ getSiblingBlocks(copyrightComment, headerKdoc, fileAnnotations, packageDirectiveNode) }) { astNode, beforeThisNode -> - FILE_INCORRECT_BLOCKS_ORDER.warnAndFix(configRules, emitWarn, isFixMode, astNode.text.lines().first(), astNode.startOffset) { + FILE_INCORRECT_BLOCKS_ORDER.warnAndFix(configRules, emitWarn, isFixMode, astNode.text.lines().first(), astNode.startOffset, astNode) { node.moveChildBefore(astNode, beforeThisNode, true) astNode.treeNext?.let { node.replaceChild(it, PsiWhiteSpaceImpl("\n\n")) } } @@ -94,7 +94,7 @@ class FileStructureRule : Rule("file-structure") { arrayOf(copyrightComment, headerKdoc, fileAnnotations, packageDirectiveNode, importsList).forEach { astNode -> astNode?.treeNext?.apply { if (elementType == WHITE_SPACE && text.count { it == '\n' } != 2) { - FILE_NO_BLANK_LINE_BETWEEN_BLOCKS.warnAndFix(configRules, emitWarn, isFixMode, astNode.text.lines().first(), astNode.startOffset) { + FILE_NO_BLANK_LINE_BETWEEN_BLOCKS.warnAndFix(configRules, emitWarn, isFixMode, astNode.text.lines().first(), astNode.startOffset, astNode) { (this as LeafPsiElement).replaceWithText("\n\n${text.replace("\n", "")}") } } @@ -107,12 +107,12 @@ class FileStructureRule : Rule("file-structure") { // importPath can be null if import name cannot be parsed, which should be a very rare case, therefore !! should be safe here imports.filter { (it.psi as KtImportDirective).importPath!!.isAllUnder }.forEach { - FILE_WILDCARD_IMPORTS.warn(configRules, emitWarn, isFixMode, it.text, it.startOffset) + FILE_WILDCARD_IMPORTS.warn(configRules, emitWarn, isFixMode, it.text, it.startOffset, it) } val sortedImports = imports.sortedBy { it.text } if (sortedImports != imports) { - FILE_UNORDERED_IMPORTS.warnAndFix(configRules, emitWarn, isFixMode, fileName, node.startOffset) { + FILE_UNORDERED_IMPORTS.warnAndFix(configRules, emitWarn, isFixMode, fileName, node.startOffset, node) { rearrangeImports(node, imports, sortedImports) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/IndentationRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/IndentationRule.kt index 3b7cc5bd4c..5a48ff5041 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/IndentationRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/IndentationRule.kt @@ -97,7 +97,7 @@ class IndentationRule : Rule("indentation") { .filter { it.textContains('\t') } .apply { if (isEmpty()) return true } .forEach { - WRONG_INDENTATION.warnAndFix(configRules, emitWarn, isFixMode, "tabs are not allowed for indentation", it.startOffset + it.text.indexOf('\t')) { + WRONG_INDENTATION.warnAndFix(configRules, emitWarn, isFixMode, "tabs are not allowed for indentation", it.startOffset + it.text.indexOf('\t'), it) { (it as LeafPsiElement).replaceWithText(it.text.replace("\t", " ".repeat(INDENT_SIZE))) } } @@ -111,7 +111,7 @@ class IndentationRule : Rule("indentation") { if (configuration.newlineAtEnd) { val lastChild = node.lastChildNode if (lastChild.elementType != WHITE_SPACE || lastChild.text.count { it == '\n' } != 1) { - WRONG_INDENTATION.warnAndFix(configRules, emitWarn, isFixMode, "no newline at the end of file $fileName", node.startOffset + node.textLength) { + WRONG_INDENTATION.warnAndFix(configRules, emitWarn, isFixMode, "no newline at the end of file $fileName", node.startOffset + node.textLength, node) { if (lastChild.elementType != WHITE_SPACE) { node.addChild(PsiWhiteSpaceImpl("\n"), null) } else { @@ -165,7 +165,7 @@ class IndentationRule : Rule("indentation") { } if (checkResult?.isCorrect != true && expectedIndent != indentError.actual) { WRONG_INDENTATION.warnAndFix(configRules, emitWarn, isFixMode, "expected $expectedIndent but was ${indentError.actual}", - whiteSpace.startOffset + whiteSpace.text.lastIndexOf('\n') + 1) { + whiteSpace.startOffset + whiteSpace.text.lastIndexOf('\n') + 1, astNode) { whiteSpace.node.indentBy(expectedIndent) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt index fa81833c47..daa2b68bc6 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt @@ -119,7 +119,7 @@ class NewlinesRule : Rule("newlines") { private fun handleSemicolon(node: ASTNode) { if (node.isEol() && node.treeParent.elementType != ENUM_ENTRY) { // semicolon at the end of line which is not part of enum members declarations - REDUNDANT_SEMICOLON.warnAndFix(configRules, emitWarn, isFixMode, node.extractLineOfText(), node.startOffset) { + REDUNDANT_SEMICOLON.warnAndFix(configRules, emitWarn, isFixMode, node.extractLineOfText(), node.startOffset, node) { node.treeParent.removeChild(node) } } @@ -135,7 +135,7 @@ class NewlinesRule : Rule("newlines") { // at the beginning of the line. if (node.prevCodeSibling()?.isFollowedByNewline() == true) { WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, - "should break a line after and not before ${node.text}", node.startOffset) { + "should break a line after and not before ${node.text}", node.startOffset, node) { node.run { treeParent.removeChild(treePrev) if (!isFollowedByNewline()) { @@ -166,7 +166,7 @@ class NewlinesRule : Rule("newlines") { } else { "should break a line before and not after ${node.text}" } - WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, freeText, node.startOffset) { + WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, freeText, node.startOffset, node) { node.selfOrOperationReferenceParent().run { if (!isBeginByNewline()) { // prepend newline @@ -193,7 +193,7 @@ class NewlinesRule : Rule("newlines") { val isNotAnonymous = parent.treeParent.elementType in listOf(CALL_EXPRESSION, PRIMARY_CONSTRUCTOR, SECONDARY_CONSTRUCTOR, FUN) if (prevWhiteSpace != null && isNotAnonymous) { WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, - "opening parentheses should not be separated from constructor or function name", node.startOffset) { + "opening parentheses should not be separated from constructor or function name", node.startOffset, node) { prevWhiteSpace.treeParent.removeChild(prevWhiteSpace) } } @@ -208,7 +208,7 @@ class NewlinesRule : Rule("newlines") { it.elementType == WHITE_SPACE && it.text.contains("\n") } if (prevNewLine != null) { - WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, "newline should be placed only after comma", node.startOffset) { + WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, "newline should be placed only after comma", node.startOffset, node) { prevNewLine.treeParent.removeChild(prevNewLine) } } @@ -226,7 +226,7 @@ class NewlinesRule : Rule("newlines") { .toList() if (newlinesBeforeArrow.isNotEmpty() || !arrowNode.isFollowedByNewline()) { WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, - "in lambda with several lines in body newline should be placed after an arrow", arrowNode.startOffset) { + "in lambda with several lines in body newline should be placed after an arrow", arrowNode.startOffset, node) { // fixme: replacement logic can be sophisticated for better appearance? newlinesBeforeArrow.forEach { it.treeParent.replaceChild(it, PsiWhiteSpaceImpl(" ")) } arrowNode.treeNext.takeIf { it.elementType == WHITE_SPACE }?.leaveOnlyOneNewLine() @@ -237,7 +237,7 @@ class NewlinesRule : Rule("newlines") { val lbraceNode = node.treeParent.firstChildNode if (!lbraceNode.isFollowedByNewline()) { WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, - "in lambda with several lines in body newline should be placed after an opening brace", lbraceNode.startOffset) { + "in lambda with several lines in body newline should be placed after an opening brace", lbraceNode.startOffset, node) { lbraceNode.treeNext.let { if (it.elementType == WHITE_SPACE) { it.leaveOnlyOneNewLine() @@ -265,7 +265,7 @@ class NewlinesRule : Rule("newlines") { .takeIf { it.size == 1 } ?.also { WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, - "functions with single return statement should be simplified to expression body", node.startOffset) { + "functions with single return statement should be simplified to expression body", node.startOffset, node) { val funNode = blockNode.treeParent // if return type is not Unit, then there should be type specification // otherwise code won't compile and colon being null is correctly invalid diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocComments.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocComments.kt index 244a553209..ad5ff8d684 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocComments.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocComments.kt @@ -79,7 +79,7 @@ class KdocComments : Rule("kdoc-comments") { val name = node.getIdentifierName() if (modifier.isAccessibleOutside() && kDoc == null) { - warning.warn(configRules, emitWarn, isFixMode, name!!.text, node.startOffset) + warning.warn(configRules, emitWarn, isFixMode, name!!.text, node.startOffset,node) } } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocFormatting.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocFormatting.kt index 7b3fc0754c..fead8f85f9 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocFormatting.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocFormatting.kt @@ -97,7 +97,7 @@ class KdocFormatting : Rule("kdoc-formatting") { val nodeAfterKdoc = kdoc?.treeNext val name = node.getFirstChildWithType(ElementType.IDENTIFIER) if (nodeAfterKdoc?.elementType == WHITE_SPACE && nodeAfterKdoc.text.countSubStringOccurrences("\n") > 1) { - BLANK_LINE_AFTER_KDOC.warnAndFix(configRules, emitWarn, isFixMode, name!!.text, nodeAfterKdoc.startOffset) { + BLANK_LINE_AFTER_KDOC.warnAndFix(configRules, emitWarn, isFixMode, name!!.text, nodeAfterKdoc.startOffset, nodeAfterKdoc) { nodeAfterKdoc.leaveOnlyOneNewLine() } } @@ -112,7 +112,7 @@ class KdocFormatting : Rule("kdoc-formatting") { KDOC_EMPTY_KDOC.warn(configRules, emitWarn, isFixMode, node.treeParent.getIdentifierName()?.text ?: node.nextSibling { it.elementType in KtTokens.KEYWORDS }?.text - ?: fileName, node.startOffset) + ?: fileName, node.startOffset, node) } return isKdocNotEmpty } @@ -121,7 +121,7 @@ class KdocFormatting : Rule("kdoc-formatting") { val kDocTags = node.kDocTags() kDocTags?.find { it.name == "deprecated" } ?.let { kDocTag -> - KDOC_NO_DEPRECATED_TAG.warnAndFix(configRules, emitWarn, isFixMode, kDocTag.text, kDocTag.node.startOffset) { + KDOC_NO_DEPRECATED_TAG.warnAndFix(configRules, emitWarn, isFixMode, kDocTag.text, kDocTag.node.startOffset, kDocTag.node) { val kDocSection = kDocTag.node.treeParent val deprecatedTagNode = kDocSection.getChildren(TokenSet.create(KDOC_TAG)) .find { "@deprecated" in it.text }!! @@ -140,7 +140,7 @@ class KdocFormatting : Rule("kdoc-formatting") { kDocTags?.filter { it.getSubjectName() == null && it.getContent().isEmpty() }?.forEach { - KDOC_NO_EMPTY_TAGS.warn(configRules, emitWarn, isFixMode, "@${it.name!!}", it.node.startOffset) + KDOC_NO_EMPTY_TAGS.warn(configRules, emitWarn, isFixMode, "@${it.name!!}", it.node.startOffset, it.node) } } @@ -155,7 +155,7 @@ class KdocFormatting : Rule("kdoc-formatting") { || tag.node.findChildAfter(KDOC_TAG_NAME, WHITE_SPACE)?.text != " " }?.forEach { tag -> KDOC_WRONG_SPACES_AFTER_TAG.warnAndFix(configRules, emitWarn, isFixMode, - "@${tag.name!!}", tag.node.startOffset) { + "@${tag.name!!}", tag.node.startOffset, tag.node) { tag.node.findChildBefore(KDOC_TEXT, WHITE_SPACE) ?.let { tag.node.replaceChild(it, LeafPsiElement(WHITE_SPACE, " ")) } tag.node.findChildAfter(KDOC_TAG_NAME, WHITE_SPACE) @@ -182,7 +182,7 @@ class KdocFormatting : Rule("kdoc-formatting") { if (kDocTags != null && !isTagsInCorrectOrder!!) { KDOC_WRONG_TAGS_ORDER.warnAndFix(configRules, emitWarn, isFixMode, - basicTags.joinToString(", ") { "@${it.name}" }, basicTags.first().node.startOffset) { + basicTags.joinToString(", ") { "@${it.name}" }, basicTags.first().node.startOffset, node) { val kDocSection = node.getFirstChildWithType(KDOC_SECTION)!! val basicTagChildren = kDocTags .filter { basicTagsOrdered.contains(it.knownTag) } @@ -212,7 +212,7 @@ class KdocFormatting : Rule("kdoc-formatting") { if (hasContentBefore xor hasEmptyLineBefore) { KDOC_NEWLINES_BEFORE_BASIC_TAGS.warnAndFix(configRules, emitWarn, isFixMode, - "@${firstBasicTag.name!!}", firstBasicTag.node.startOffset) { + "@${firstBasicTag.name!!}", firstBasicTag.node.startOffset, firstBasicTag.node) { if (hasContentBefore) { if (previousTag != null) { previousTag.applyToPrevSibling(KDOC_LEADING_ASTERISK) { @@ -248,7 +248,7 @@ class KdocFormatting : Rule("kdoc-formatting") { tagsWithRedundantEmptyLines.forEach { tag -> KDOC_NO_NEWLINES_BETWEEN_BASIC_TAGS.warnAndFix(configRules, emitWarn, isFixMode, - "@${tag.name}", tag.startOffset) { + "@${tag.name}", tag.startOffset, tag.node) { tag.node.nextSibling { it.elementType == WHITE_SPACE }?.leaveOnlyOneNewLine() // the first asterisk before tag is not included inside KDOC_TAG node // we look for the second and take its previous which should be WHITE_SPACE with newline @@ -276,7 +276,7 @@ class KdocFormatting : Rule("kdoc-formatting") { if (presentSpecialTagNodes != null && poorlyFormattedTagNodes!!.isNotEmpty()) { KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS.warnAndFix(configRules, emitWarn, isFixMode, poorlyFormattedTagNodes.joinToString(", ") { "@${(it.psi as KDocTag).name!!}" }, - poorlyFormattedTagNodes.first().startOffset) { + poorlyFormattedTagNodes.first().startOffset, node) { poorlyFormattedTagNodes.forEach { node -> while (node.lastChildNode.elementType == KDOC_LEADING_ASTERISK && node.lastChildNode.treePrev.treePrev.elementType == KDOC_LEADING_ASTERISK) { diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocMethods.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocMethods.kt index f2c87a9a9a..30dd5247c8 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocMethods.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocMethods.kt @@ -106,7 +106,7 @@ class KdocMethods : Rule("kdoc-methods") { if (kDoc == null && anyTagFailed) { addKdocTemplate(node, name, missingParameters, explicitlyThrownExceptions, returnCheckFailed) } else if (kDoc == null) { - MISSING_KDOC_ON_FUNCTION.warn(configRules, emitWarn, false, name, node.startOffset) + MISSING_KDOC_ON_FUNCTION.warn(configRules, emitWarn, false, name, node.startOffset, node) } } @@ -151,7 +151,7 @@ class KdocMethods : Rule("kdoc-methods") { missingParameters: Collection, kDocTags: Collection?) { KDOC_WITHOUT_PARAM_TAG.warnAndFix(configRules, emitWarn, isFixMode, - "$name (${missingParameters.joinToString()})", node.startOffset) { + "$name (${missingParameters.joinToString()})", node.startOffset, node) { val beforeTag = kDocTags?.find { it.knownTag == KDocKnownTag.RETURN } ?: kDocTags?.find { it.knownTag == KDocKnownTag.THROWS } missingParameters.forEach { @@ -169,7 +169,7 @@ class KdocMethods : Rule("kdoc-methods") { kDoc: ASTNode?, kDocTags: Collection? ) { - KDOC_WITHOUT_RETURN_TAG.warnAndFix(configRules, emitWarn, isFixMode, name, node.startOffset) { + KDOC_WITHOUT_RETURN_TAG.warnAndFix(configRules, emitWarn, isFixMode, name, node.startOffset, node) { val beforeTag = kDocTags?.find { it.knownTag == KDocKnownTag.THROWS } kDoc?.insertTagBefore(beforeTag?.node) { addChild(LeafPsiElement(KDOC_TAG_NAME, "@return")) @@ -183,7 +183,7 @@ class KdocMethods : Rule("kdoc-methods") { missingExceptions: Collection ) { KDOC_WITHOUT_THROWS_TAG.warnAndFix(configRules, emitWarn, isFixMode, - "$name (${missingExceptions.joinToString()})", node.startOffset) { + "$name (${missingExceptions.joinToString()})", node.startOffset, node) { missingExceptions.forEach { kDoc?.insertTagBefore(null) { addChild(LeafPsiElement(KDOC_TAG_NAME, "@throws")) @@ -200,7 +200,7 @@ class KdocMethods : Rule("kdoc-methods") { explicitlyThrownExceptions: Collection, returnCheckFailed: Boolean ) { - MISSING_KDOC_ON_FUNCTION.warnAndFix(configRules, emitWarn, isFixMode, name, node.startOffset) { + MISSING_KDOC_ON_FUNCTION.warnAndFix(configRules, emitWarn, isFixMode, name, node.startOffset, node) { val kDocTemplate = "/**\n" + (missingParameters.joinToString("") { " * @param $it\n" } + (if (returnCheckFailed) " * @return\n" else "") + @@ -218,7 +218,7 @@ class KdocMethods : Rule("kdoc-methods") { if (kdocTextNodes.size == 1) { val kdocText = kdocTextNodes.first().text.trim() if (kdocText.matches(uselessKdocRegex)) { - KDOC_TRIVIAL_KDOC_ON_FUNCTION.warn(configRules, emitWarn, isFixMode, kdocText, kdocTextNodes.first().startOffset) + KDOC_TRIVIAL_KDOC_ON_FUNCTION.warn(configRules, emitWarn, isFixMode, kdocText, kdocTextNodes.first().startOffset, node) } } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index c39df3a897..7ab526403c 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -293,11 +293,16 @@ fun ASTNode?.isAccessibleOutside(): Boolean = true } -fun ASTNode.hasSuppress(warningName: String): Boolean = parent(ANNOTATION) - .let { node -> - node?.getAllChildrenWithType(ANNOTATION_ENTRY)!!.any { it.text.contains("Suppress") - && it.text.contains(warningName) } +fun ASTNode.hasSuppress(warningName: String): Boolean { + if (elementType == FILE) { + return findAllNodesWithSpecificType(ANNOTATION_ENTRY).any { + it.text.contains("Suppress") && it.text.contains(warningName) } + } + return parent({ it.findAllNodesWithSpecificType(ANNOTATION_ENTRY).any { + it.text.contains("Suppress") && it.text.contains(warningName) + }}) != null +} /** * removing all newlines in WHITE_SPACE node and replacing it to a one newline saving the initial indenting format */ diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt new file mode 100644 index 0000000000..81d8966cdb --- /dev/null +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt @@ -0,0 +1,25 @@ +package org.cqfn.diktat.ruleset.utils + +import org.cqfn.diktat.ruleset.rules.DIKTAT_RULE_SET_ID +import org.cqfn.diktat.ruleset.rules.IdentifierNaming +import org.cqfn.diktat.util.lintMethod +import org.junit.jupiter.api.Test + +class SuppressTest { + + private val ruleId: String = "$DIKTAT_RULE_SET_ID:identifier-naming" + + @Test + fun `test suppress`() { + val code = + """ + @Suppress("FUNCTION_NAME_INCORRECT_CASE") + class SomeClass { + fun /* */ methODTREE(): String { + + } + } + """.trimIndent() + lintMethod(IdentifierNaming(), code) + } +} \ No newline at end of file From 3051dcce4e35b1a18597dcc111e980f68427d94c Mon Sep 17 00:00:00 2001 From: aktsay Date: Tue, 8 Sep 2020 16:48:53 +0300 Subject: [PATCH 03/20] feature/suppress-in-individual-files ### What's done: * Warn method remade * Added suppress check * Added test --- .../cqfn/diktat/ruleset/constants/Warnings.kt | 13 ++++++++-- .../ruleset/rules/BlockStructureBraces.kt | 8 +++--- .../rules/BracesInConditionalsAndLoopsRule.kt | 8 +++--- .../rules/ClassLikeStructuresOrderRule.kt | 4 +-- .../ruleset/rules/ConsecutiveSpacesRule.kt | 2 +- .../cqfn/diktat/ruleset/rules/EmptyBlock.kt | 6 ++--- .../diktat/ruleset/rules/EnumsSeparated.kt | 8 +++--- .../cqfn/diktat/ruleset/rules/FileNaming.kt | 4 +-- .../diktat/ruleset/rules/IdentifierNaming.kt | 26 +++++++++---------- .../cqfn/diktat/ruleset/rules/LineLength.kt | 2 +- .../diktat/ruleset/rules/PackageNaming.kt | 12 ++++----- .../ruleset/rules/SingleLineStatementsRule.kt | 2 +- .../ruleset/rules/StringConcatenationRule.kt | 2 +- .../diktat/ruleset/rules/WhiteSpaceRule.kt | 12 ++++----- .../ruleset/rules/comments/CommentsRule.kt | 2 +- .../rules/comments/HeaderCommentRule.kt | 10 +++---- .../ruleset/rules/files/BlankLinesRule.kt | 4 +-- .../diktat/ruleset/rules/files/FileSize.kt | 2 +- .../ruleset/rules/files/FileStructureRule.kt | 10 +++---- .../ruleset/rules/files/IndentationRule.kt | 6 ++--- .../ruleset/rules/files/NewlinesRule.kt | 16 ++++++------ .../diktat/ruleset/rules/kdoc/KdocComments.kt | 2 +- .../ruleset/rules/kdoc/KdocFormatting.kt | 18 ++++++------- .../diktat/ruleset/rules/kdoc/KdocMethods.kt | 12 ++++----- .../cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 13 +++++++--- .../cqfn/diktat/ruleset/utils/SuppressTest.kt | 22 ++++++++++++++++ 26 files changed, 131 insertions(+), 95 deletions(-) create mode 100644 diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt index d4916b19be..d357de78e4 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt @@ -3,6 +3,8 @@ package org.cqfn.diktat.ruleset.constants import org.cqfn.diktat.common.config.rules.Rule import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.common.config.rules.isRuleEnabled +import org.cqfn.diktat.ruleset.utils.hasSuppress +import org.jetbrains.kotlin.com.intellij.lang.ASTNode @Suppress("ForbiddenComment", "MagicNumber") enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: String) : Rule { @@ -90,8 +92,11 @@ enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: S isFixMode: Boolean, freeText: String, offset: Int, + node: ASTNode?, autoFix: () -> Unit) { - warn(configRules, emit, this.canBeAutoCorrected, freeText, offset) + if (node != null && node.hasSuppress(name)) + return + warn(configRules, emit, this.canBeAutoCorrected, freeText, offset, node) fix(configRules, autoFix, isFixMode) } @@ -99,7 +104,11 @@ enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: S emit: ((offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit), autoCorrected: Boolean, freeText: String, - offset: Int) { + offset: Int, + node: ASTNode?) { + if (node != null && node.hasSuppress(name)) + return + if (configs.isRuleEnabled(this)) { emit(offset, "${this.warnText()} $freeText", diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BlockStructureBraces.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BlockStructureBraces.kt index 966efdc00a..e0de97f24d 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BlockStructureBraces.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BlockStructureBraces.kt @@ -155,7 +155,7 @@ class BlockStructureBraces : Rule("block-structure") { val braceSpace = nodeBefore?.treePrev if (braceSpace == null || checkBraceNode(braceSpace, true)) BRACES_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "incorrect newline before opening brace", - (braceSpace ?: node).startOffset) { + (braceSpace ?: node).startOffset, node) { if (braceSpace == null || braceSpace.elementType != WHITE_SPACE) { node.addChild(PsiWhiteSpaceImpl(" "), nodeBefore) } else { @@ -170,7 +170,7 @@ class BlockStructureBraces : Rule("block-structure") { node.findChildByType(beforeType) else node)?.findLBrace()?.treeNext ?: return if (checkBraceNode(newNode)) { BRACES_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "incorrect same line after opening brace", - newNode.startOffset) { + newNode.startOffset, node) { if (newNode.elementType != WHITE_SPACE) { node.addChild(PsiWhiteSpaceImpl("\n"), newNode) } else { @@ -184,7 +184,7 @@ class BlockStructureBraces : Rule("block-structure") { allMiddleSpace.forEach { if (checkBraceNode(it, true)) { BRACES_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "incorrect new line after closing brace", - it.startOffset) { + it.startOffset, node) { if (it.elementType != WHITE_SPACE) { node.addChild(PsiWhiteSpaceImpl(" "), node.findChildByType(keyword)) } else { @@ -200,7 +200,7 @@ class BlockStructureBraces : Rule("block-structure") { val space = node.findChildByType(RBRACE)!!.treePrev if (checkBraceNode(space)) BRACES_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "no newline before closing brace", - (space.treeNext ?: node.findChildByType(RBRACE))!!.startOffset) { + (space.treeNext ?: node.findChildByType(RBRACE))!!.startOffset, node) { if (space.elementType != WHITE_SPACE) { node.addChild(PsiWhiteSpaceImpl("\n"), node.findChildByType(RBRACE)) } else { diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BracesInConditionalsAndLoopsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BracesInConditionalsAndLoopsRule.kt index 4eb224c3de..2af9c9891a 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BracesInConditionalsAndLoopsRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BracesInConditionalsAndLoopsRule.kt @@ -74,7 +74,7 @@ class BracesInConditionalsAndLoopsRule : Rule("braces-rule") { if (thenNode?.elementType != BLOCK) { NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnAndFix(configRules, emitWarn, isFixMode, "IF", - (thenNode?.prevSibling { it.elementType == IF_KEYWORD } ?: node).startOffset) { + (thenNode?.prevSibling { it.elementType == IF_KEYWORD } ?: node).startOffset, node) { if (thenNode != null) { ifPsi.then!!.replaceWithBlock(indent) if (elseNode != null) { @@ -89,7 +89,7 @@ class BracesInConditionalsAndLoopsRule : Rule("braces-rule") { if (hasElseBranch && elseNode?.elementType != IF && elseNode?.elementType != BLOCK) { NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnAndFix(configRules, emitWarn, isFixMode, "ELSE", - (elseNode?.treeParent?.prevSibling { it.elementType == ELSE_KEYWORD } ?: node).startOffset) { + (elseNode?.treeParent?.prevSibling { it.elementType == ELSE_KEYWORD } ?: node).startOffset, node) { if (elseNode != null) { ifPsi.`else`!!.replaceWithBlock(indent) } else { @@ -104,7 +104,7 @@ class BracesInConditionalsAndLoopsRule : Rule("braces-rule") { val loopBody = (node.psi as KtLoopExpression).body val loopBodyNode = loopBody?.node if (loopBodyNode == null || loopBodyNode.elementType != BLOCK) { - NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnAndFix(configRules, emitWarn, isFixMode, node.elementType.toString(), node.startOffset) { + NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnAndFix(configRules, emitWarn, isFixMode, node.elementType.toString(), node.startOffset, node) { // fixme proper way to calculate indent? or get step size (instead of hardcoded 4) val indent = node.prevSibling { it.elementType == WHITE_SPACE }!!.text.lines().last().count { it == ' ' } if (loopBody != null) { @@ -127,7 +127,7 @@ class BracesInConditionalsAndLoopsRule : Rule("braces-rule") { .map { it.expression as KtBlockExpression } .filter { it.statements.size == 1 } .forEach { - NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnAndFix(configRules, emitWarn, isFixMode, "WHEN", it.node.startOffset) { + NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnAndFix(configRules, emitWarn, isFixMode, "WHEN", it.node.startOffset, node) { it.astReplace(it.firstStatement!!.node.psi) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ClassLikeStructuresOrderRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ClassLikeStructuresOrderRule.kt index cb9a1d6af3..e7d20e582a 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ClassLikeStructuresOrderRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ClassLikeStructuresOrderRule.kt @@ -85,7 +85,7 @@ class ClassLikeStructuresOrderRule : Rule("class-like-structures") { unusedClasses) blocks.allBlockFlattened().reversed().handleIncorrectOrder(blocks::getSiblingBlocks) { astNode, beforeThisNode -> - WRONG_ORDER_IN_CLASS_LIKE_STRUCTURES.warnAndFix(configRules, emitWarn, isFixMode, astNode.elementType.toString() + ": " + astNode.text, astNode.startOffset) { + WRONG_ORDER_IN_CLASS_LIKE_STRUCTURES.warnAndFix(configRules, emitWarn, isFixMode, astNode.elementType.toString() + ": " + astNode.text, astNode.startOffset, astNode) { val replacement = node.moveChildBefore(astNode, beforeThisNode, true) replacement.oldNodes.forEachIndexed { idx, oldNode -> blocks.allBlocks().find { oldNode in it }?.apply { @@ -104,7 +104,7 @@ class ClassLikeStructuresOrderRule : Rule("class-like-structures") { val whiteSpaceBefore = previousProperty.nextSibling { it.elementType == WHITE_SPACE }!! val nRequiredNewLines = if (commentOnThis == null) 1 else 2 if (whiteSpaceBefore.text.count { it == '\n' } != nRequiredNewLines) - BLANK_LINE_BETWEEN_PROPERTIES.warnAndFix(configRules, emitWarn, isFixMode, node.getIdentifierName()!!.text, node.startOffset) { + BLANK_LINE_BETWEEN_PROPERTIES.warnAndFix(configRules, emitWarn, isFixMode, node.getIdentifierName()!!.text, node.startOffset, node) { whiteSpaceBefore.leaveExactlyNumNewLines(nRequiredNewLines) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ConsecutiveSpacesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ConsecutiveSpacesRule.kt index ab21108858..5dceda5b5b 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ConsecutiveSpacesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/ConsecutiveSpacesRule.kt @@ -76,7 +76,7 @@ class ConsecutiveSpacesRule : Rule("too-many-spaces") { if (spaces > configuration.numberOfSpaces && !node.isWhiteSpaceWithNewline() && !node.hasEolComment()) { TOO_MANY_CONSECUTIVE_SPACES.warnAndFix(configRules, emitWarn, isFixMode, - "found: $spaces. need to be: ${configuration.numberOfSpaces}", node.startOffset) { + "found: $spaces. need to be: ${configuration.numberOfSpaces}", node.startOffset, node) { node.squeezeSpaces() } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EmptyBlock.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EmptyBlock.kt index b9186e34a8..87b0986286 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EmptyBlock.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EmptyBlock.kt @@ -48,12 +48,12 @@ class EmptyBlock : Rule("empty-block-structure") { if (node.isBlockEmpty()) { if (!configuration.emptyBlockExist) { EMPTY_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "empty blocks are forbidden unless it is function with override keyword", - node.startOffset) {} + node.startOffset, node) {} } else { val space = node.findChildByType(RBRACE)!!.treePrev if (configuration.emptyBlockNewline && !space.text.contains("\n")) { EMPTY_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "different style for empty block", - node.startOffset) { + node.startOffset, node) { if (space.elementType == WHITE_SPACE) (space.treeNext as LeafPsiElement).replaceWithText("\n") else @@ -61,7 +61,7 @@ class EmptyBlock : Rule("empty-block-structure") { } } else if (!configuration.emptyBlockNewline && space.text.contains("\n")) { EMPTY_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "different style for empty block", - node.startOffset) { + node.startOffset, node) { node.removeChild(space) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EnumsSeparated.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EnumsSeparated.kt index a3bd13c065..b57aace6de 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EnumsSeparated.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EnumsSeparated.kt @@ -55,7 +55,7 @@ class EnumsSeparated : Rule("enum-separated") { enumEntries.forEach { if (!it.treeNext.isWhiteSpaceWithNewline()) ENUMS_SEPARATED.warnAndFix(configRules, emitWarn, isFixMode, "enum entries must end with a line break", - it.startOffset) { + it.startOffset, node) { it.appendNewlineMergingWhiteSpace(it.treeNext, it.treeNext) } } @@ -76,19 +76,19 @@ class EnumsSeparated : Rule("enum-separated") { private fun checkLastEnum(node: ASTNode) { if (!node.hasChildOfType(SEMICOLON)) { ENUMS_SEPARATED.warnAndFix(configRules, emitWarn, isFixMode, "enums must end with semicolon", - node.startOffset) { + node.startOffset, node) { node.addChild(LeafPsiElement(SEMICOLON, ";"), null) node.addChild(PsiWhiteSpaceImpl("\n"), node.findChildByType(SEMICOLON)!!) } } else if (!node.findChildByType(SEMICOLON)!!.treePrev.isWhiteSpaceWithNewline()) { ENUMS_SEPARATED.warnAndFix(configRules, emitWarn, isFixMode, "semicolon must be on a new line", - node.startOffset) { + node.startOffset, node) { node.appendNewlineMergingWhiteSpace(node.findChildByType(SEMICOLON)!!, node.findChildByType(SEMICOLON)!!) } } if (!node.hasChildOfType(COMMA)) { ENUMS_SEPARATED.warnAndFix(configRules, emitWarn, isFixMode, "last enum entry must end with a comma", - node.startOffset) { + node.startOffset, node) { node.addChild(LeafPsiElement(COMMA, ","), node.findChildByType(SEMICOLON)!!.treePrev) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/FileNaming.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/FileNaming.kt index 6a9de3bca2..3dd258db67 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/FileNaming.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/FileNaming.kt @@ -54,7 +54,7 @@ class FileNaming : Rule("file-naming") { if (fileName != null) { val (name, extension) = getFileParts() if (!name.isPascalCase() || !VALID_EXTENSIONS.contains(extension)) { - FILE_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, "$name$extension", 0) { + FILE_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, "$name$extension", 0, null) { // FixMe: we can add an autocorrect here in future, but is there any purpose to change file or class name? } } @@ -68,7 +68,7 @@ class FileNaming : Rule("file-naming") { if (classes.size == 1) { val className = classes[0].getFirstChildWithType(IDENTIFIER)!!.text if (className != fileNameWithoutSuffix) { - FILE_NAME_MATCH_CLASS.warnAndFix(configRules, emitWarn, isFixMode, "$fileNameWithoutSuffix$fileNameSuffix vs $className", 0) { + FILE_NAME_MATCH_CLASS.warnAndFix(configRules, emitWarn, isFixMode, "$fileNameWithoutSuffix$fileNameSuffix vs $className", 0, fileLevelNode) { // FixMe: we can add an autocorrect here in future, but is there any purpose to change file name? } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt index f8f6803e8a..e93e9c076e 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt @@ -107,7 +107,7 @@ class IdentifierNaming : Rule("identifier-naming") { if (identifierText?.startsWith('`') == true && identifierText.endsWith('`')) { // the only exception is test method with @Test annotation if (!(node.elementType == ElementType.FUN && node.hasTestAnnotation())) { - BACKTICKS_PROHIBITED.warn(configRules, emitWarn, isFixMode, identifierText, identifier.startOffset) + BACKTICKS_PROHIBITED.warn(configRules, emitWarn, isFixMode, identifierText, identifier.startOffset, node) } return true } @@ -126,19 +126,19 @@ class IdentifierNaming : Rule("identifier-naming") { // variable should not contain only one letter in it's name. This is a bad example: b512 // but no need to raise a warning here if length of a variable. In this case we will raise IDENTIFIER_LENGTH if (variableName.text.containsOneLetterOrZero() && variableName.text.length > 1) { - VARIABLE_NAME_INCORRECT.warn(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset) + VARIABLE_NAME_INCORRECT.warn(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset, node) } // check for constant variables - check for val from companion object or on global file level // it should be in UPPER_CASE, no need to raise this warning if it is one-letter variable if (node.isConstant()) { if (!variableName.text.isUpperSnakeCase() && variableName.text.length > 1) { - CONSTANT_UPPERCASE.warnAndFix(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset) { + CONSTANT_UPPERCASE.warnAndFix(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset,node) { (variableName as LeafPsiElement).replaceWithText(variableName.text.toUpperSnakeCase()) } } } else if (!variableName.text.isLowerCamelCase()) { // variable name should be in camel case. The only exception is a list of industry standard variables like i, j, k. - VARIABLE_NAME_INCORRECT_FORMAT.warnAndFix(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset) { + VARIABLE_NAME_INCORRECT_FORMAT.warnAndFix(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset, node) { // FixMe: cover fixes with tests (variableName as LeafPsiElement).replaceWithText(variableName.text.toLowerCamelCase()) } @@ -152,7 +152,7 @@ class IdentifierNaming : Rule("identifier-naming") { .forEach { variableName -> // generally, variables with prefixes are not allowed (like mVariable, xCode, iValue) if (variableName.text.hasPrefix()) { - VARIABLE_HAS_PREFIX.warnAndFix(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset) { + VARIABLE_HAS_PREFIX.warnAndFix(configRules, emitWarn, isFixMode, variableName.text, variableName.startOffset, node) { (variableName as LeafPsiElement).replaceWithText(variableName.text.removePrefix()) } } @@ -189,14 +189,14 @@ class IdentifierNaming : Rule("identifier-naming") { private fun checkCLassNamings(node: ASTNode): List { val genericType: ASTNode? = node.getTypeParameterList() if (genericType != null && !validGenericTypeName(genericType.text)) { - GENERIC_NAME.warnAndFix(configRules, emitWarn, isFixMode, genericType.text, genericType.startOffset) { + GENERIC_NAME.warnAndFix(configRules, emitWarn, isFixMode, genericType.text, genericType.startOffset, node) { // FixMe: should fix generic name here } } val className: ASTNode? = node.getIdentifierName() if (!(className!!.text.isPascalCase())) { - CLASS_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, className.text, className.startOffset) { + CLASS_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, className.text, className.startOffset, node) { (className as LeafPsiElement).replaceWithText(className.text.toPascalCase()) } } @@ -221,7 +221,7 @@ class IdentifierNaming : Rule("identifier-naming") { ?.text if (superClassName != null && hasExceptionSuffix(superClassName) && !hasExceptionSuffix(classNameNode!!.text)) { - EXCEPTION_SUFFIX.warnAndFix(configRules, emitWarn, isFixMode, classNameNode.text, classNameNode.startOffset) { + EXCEPTION_SUFFIX.warnAndFix(configRules, emitWarn, isFixMode, classNameNode.text, classNameNode.startOffset, node) { // FixMe: need to add tests for this (classNameNode as LeafPsiElement).replaceWithText(classNameNode.text + "Exception") } @@ -236,7 +236,7 @@ class IdentifierNaming : Rule("identifier-naming") { // if this object is companion object or anonymous object - it does not have any name val objectName: ASTNode = node.getIdentifierName() ?: return listOf() if (!objectName.text.isPascalCase()) { - OBJECT_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, objectName.text, objectName.startOffset) { + OBJECT_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, objectName.text, objectName.startOffset, node) { (objectName as LeafPsiElement).replaceWithText(objectName.text.toPascalCase()) } } @@ -252,7 +252,7 @@ class IdentifierNaming : Rule("identifier-naming") { val enumValues: List = node.getChildren(null).filter { it.elementType == ElementType.IDENTIFIER } enumValues.forEach { value -> if (!value.text.isUpperSnakeCase()) { - ENUM_VALUE.warnAndFix(configRules, emitWarn, isFixMode, value.text, value.startOffset) { + ENUM_VALUE.warnAndFix(configRules, emitWarn, isFixMode, value.text, value.startOffset, node) { // FixMe: add tests for this (value as LeafPsiElement).replaceWithText(value.text.toUpperSnakeCase()) } @@ -274,7 +274,7 @@ class IdentifierNaming : Rule("identifier-naming") { // basic check for camel case if (!functionName.text.isLowerCamelCase()) { - FUNCTION_NAME_INCORRECT_CASE.warnAndFix(configRules, emitWarn, isFixMode, functionName.text, functionName.startOffset) { + FUNCTION_NAME_INCORRECT_CASE.warnAndFix(configRules, emitWarn, isFixMode, functionName.text, functionName.startOffset, node) { // FixMe: add tests for this (functionName as LeafPsiElement).replaceWithText(functionName.text.toLowerCamelCase()) } @@ -286,7 +286,7 @@ class IdentifierNaming : Rule("identifier-naming") { // if function has Boolean return type in 99% of cases it is much better to name it with isXXX or hasXXX prefix if (functionReturnType != null && functionReturnType == PrimitiveType.BOOLEAN.typeName.asString()) { if (BOOLEAN_METHOD_PREFIXES.none { functionName.text.startsWith(it) }) { - FUNCTION_BOOLEAN_PREFIX.warnAndFix(configRules, emitWarn, isFixMode, functionName.text, functionName.startOffset) { + FUNCTION_BOOLEAN_PREFIX.warnAndFix(configRules, emitWarn, isFixMode, functionName.text, functionName.startOffset, node) { // FixMe: add agressive autofix for this } } @@ -319,7 +319,7 @@ class IdentifierNaming : Rule("identifier-naming") { ONE_CHAR_IDENTIFIERS.contains(it.text) && isVariable || validCatchIdentifier(it) )) { - IDENTIFIER_LENGTH.warn(configRules, emitWarn, isFixMode, it.text, it.startOffset) + IDENTIFIER_LENGTH.warn(configRules, emitWarn, isFixMode, it.text, it.startOffset, it) } } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LineLength.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LineLength.kt index 82593ba8c4..0d6ca734fb 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LineLength.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LineLength.kt @@ -54,7 +54,7 @@ class LineLength : Rule("line-length") { if ((newNode.elementType != KDOC_TEXT && newNode.elementType != KDOC_MARKDOWN_INLINE_LINK) || !isKDocValid(newNode)) { LONG_LINE.warnAndFix(configRules, emitWarn, isFixMode, "max line length ${configuration.lineLength}, but was ${it.length}", - offset + node.startOffset) {} + offset + node.startOffset, node) {} } } offset += it.length + 1 diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/PackageNaming.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/PackageNaming.kt index 5d8bb82643..ecaa79d499 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/PackageNaming.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/PackageNaming.kt @@ -90,7 +90,7 @@ class PackageNaming : Rule("package-naming") { * checking and fixing the case when package directive is missing in the file */ private fun checkMissingPackageName(packageDirectiveNode: ASTNode, realPackageName: List, fileName: String) { - PACKAGE_NAME_MISSING.warnAndFix(configRules, emitWarn, isFixMode, fileName, packageDirectiveNode.startOffset) { + PACKAGE_NAME_MISSING.warnAndFix(configRules, emitWarn, isFixMode, fileName, packageDirectiveNode.startOffset, packageDirectiveNode) { if (realPackageName.isNotEmpty()) { packageDirectiveNode.addChild(LeafPsiElement(PACKAGE_KEYWORD, PACKAGE_KEYWORD.toString()), null) packageDirectiveNode.addChild(PsiWhiteSpaceImpl(" "), null) @@ -135,14 +135,14 @@ class PackageNaming : Rule("package-naming") { wordsInPackageName .filter { word -> word.text.hasUppercaseLetter() } .forEach { - PACKAGE_NAME_INCORRECT_CASE.warnAndFix(configRules, emitWarn, isFixMode, it.text, it.startOffset) { + PACKAGE_NAME_INCORRECT_CASE.warnAndFix(configRules, emitWarn, isFixMode, it.text, it.startOffset, it) { it.toLower() } } // package name should start from a company's domain name if (!isDomainMatches(wordsInPackageName)) { - PACKAGE_NAME_INCORRECT_PREFIX.warnAndFix(configRules, emitWarn, isFixMode, domainName, wordsInPackageName[0].startOffset) { + PACKAGE_NAME_INCORRECT_PREFIX.warnAndFix(configRules, emitWarn, isFixMode, domainName, wordsInPackageName[0].startOffset, wordsInPackageName[0]) { val parentNodeToInsert = wordsInPackageName[0].parent(DOT_QUALIFIED_EXPRESSION)!! createAndInsertPackageName(parentNodeToInsert, wordsInPackageName[0].treeParent, domainName.split(PACKAGE_SEPARATOR)) } @@ -151,7 +151,7 @@ class PackageNaming : Rule("package-naming") { // all words should contain only ASCII letters or digits wordsInPackageName .filter { word -> !correctSymbolsAreUsed(word.text) } - .forEach { PACKAGE_NAME_INCORRECT_SYMBOLS.warn(configRules, emitWarn, isFixMode, it.text, it.startOffset) } + .forEach { PACKAGE_NAME_INCORRECT_SYMBOLS.warn(configRules, emitWarn, isFixMode, it.text, it.startOffset, it) } // all words should contain only ASCII letters or digits wordsInPackageName.forEach { correctPackageWordSeparatorsUsed(it) } @@ -172,7 +172,7 @@ class PackageNaming : Rule("package-naming") { */ private fun correctPackageWordSeparatorsUsed(word: ASTNode) { if (word.text.contains("_") && !exceptionForUnderscore(word.text)) { - INCORRECT_PACKAGE_SEPARATOR.warnAndFix(configRules, emitWarn, isFixMode, word.text, word.startOffset) { + INCORRECT_PACKAGE_SEPARATOR.warnAndFix(configRules, emitWarn, isFixMode, word.text, word.startOffset, word) { (word as LeafPsiElement).replaceWithText(word.text.replace("_", "")) } } @@ -239,7 +239,7 @@ class PackageNaming : Rule("package-naming") { */ private fun checkFilePathMatchesWithPackageName(packageNameParts: List, realName: List) { if (realName.isNotEmpty() && packageNameParts.map { node -> node.text } != realName) { - PACKAGE_NAME_INCORRECT_PATH.warnAndFix(configRules, emitWarn, isFixMode, realName.joinToString(PACKAGE_SEPARATOR), packageNameParts[0].startOffset) { + PACKAGE_NAME_INCORRECT_PATH.warnAndFix(configRules, emitWarn, isFixMode, realName.joinToString(PACKAGE_SEPARATOR), packageNameParts[0].startOffset, packageNameParts[0]) { // need to get first top-level DOT-QUALIFIED-EXPRESSION // -- PACKAGE_DIRECTIVE // -- DOT_QUALIFIED_EXPRESSION diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SingleLineStatementsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SingleLineStatementsRule.kt index 011500f738..3957d9dbc2 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SingleLineStatementsRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SingleLineStatementsRule.kt @@ -44,7 +44,7 @@ class SingleLineStatementsRule : Rule("statement") { node.getChildren(semicolonToken).forEach { if (!it.isFollowedByNewline()) { MORE_THAN_ONE_STATEMENT_PER_LINE.warnAndFix(configRules, emitWarn, isFixMode, it.extractLineOfText(), - it.startOffset) { + it.startOffset, it) { if (it.treeParent.elementType == ENUM_ENTRY) { node.treeParent.addChild(PsiWhiteSpaceImpl("\n"), node.treeNext) } else { diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/StringConcatenationRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/StringConcatenationRule.kt index a472ec610e..11188fe627 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/StringConcatenationRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/StringConcatenationRule.kt @@ -54,7 +54,7 @@ class StringConcatenationRule : Rule("string-concatenation") { assert(node.elementType == BINARY_EXPRESSION) val firstChild = node.firstChildNode return if (isPlusBinaryExpression(node) && firstChild.elementType == STRING_TEMPLATE) { - STRING_CONCATENATION.warn(configRules, emitWarn, this.isFixMode, parentNode.text, firstChild.startOffset) + STRING_CONCATENATION.warn(configRules, emitWarn, this.isFixMode, parentNode.text, firstChild.startOffset, firstChild) true } else { false diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/WhiteSpaceRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/WhiteSpaceRule.kt index 9fb11f9401..9a3b2fa334 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/WhiteSpaceRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/WhiteSpaceRule.kt @@ -133,7 +133,7 @@ class WhiteSpaceRule : Rule("horizontal-whitespace") { if (node.treeNext.numWhiteSpaces()?.let { it > 0 } == true) { // there is either whitespace or newline after constructor keyword WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, "keyword '${node.text}' should not be separated from " + - "'(' with a whitespace", node.startOffset) { + "'(' with a whitespace", node.startOffset, node) { node.treeParent.removeChild(node.treeNext) } } @@ -148,7 +148,7 @@ class WhiteSpaceRule : Rule("horizontal-whitespace") { return } WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, "keyword '${node.text}' should be separated from " + - "'${nextCodeLeaf.text}' with a whitespace", nextCodeLeaf.startOffset) { + "'${nextCodeLeaf.text}' with a whitespace", nextCodeLeaf.startOffset, node) { node.leaveSingleWhiteSpace() } } @@ -176,13 +176,13 @@ class WhiteSpaceRule : Rule("horizontal-whitespace") { if (isFromLambdaAsArgument) { if (numWhiteSpace != 0) { WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, "there should be no whitespace before '{' of lambda" + - " inside argument list", node.startOffset) { + " inside argument list", node.startOffset, node) { whitespaceOrPrevNode.treeParent.removeChild(whitespaceOrPrevNode) } } } else if (prevNode.elementType !in keywordsWithSpaceAfter) { if (numWhiteSpace != 1) { - WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, "there should be a whitespace before '{'", node.startOffset) { + WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, "there should be a whitespace before '{'", node.startOffset, node) { prevNode.leaveSingleWhiteSpace() } } @@ -223,7 +223,7 @@ class WhiteSpaceRule : Rule("horizontal-whitespace") { getDescription(requiredSpacesBefore != null, requiredSpacesAfter != null, requiredSpacesBefore, requiredSpacesAfter) + ", but has" + getDescription(isErrorBefore, isErrorAfter, spacesBefore, spacesAfter) - WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, freeText, node.startOffset) { + WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, freeText, node.startOffset, node) { node.fixSpaceAround(requiredSpacesBefore, requiredSpacesAfter) } } @@ -234,7 +234,7 @@ class WhiteSpaceRule : Rule("horizontal-whitespace") { // the second condition corresponds to the last line of file val isEol = node.textContains('\n') || node.psi.parentsWithSelf.all { it.nextSibling == null } if (hasSpaces && isEol) { - WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, "there should be no spaces in the end of line", node.startOffset) { + WRONG_WHITESPACE.warnAndFix(configRules, emitWarn, isFixMode, "there should be no spaces in the end of line", node.startOffset, node) { (node as LeafElement).replaceWithText(node.text.trimStart(' ')) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt index d9dd7042ec..a24de1bd02 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt @@ -88,7 +88,7 @@ class CommentsRule : Rule("comments") { .filter { (_, parsedNode) -> parsedNode.findAllNodesWithSpecificType(TokenType.ERROR_ELEMENT).isEmpty() }.forEach { (offset, parsedNode) -> - COMMENTED_OUT_CODE.warn(configRules, emitWarn, isFixMode, parsedNode.text.substringBefore("\n").trim(), offset) + COMMENTED_OUT_CODE.warn(configRules, emitWarn, isFixMode, parsedNode.text.substringBefore("\n").trim(), offset, node) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/HeaderCommentRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/HeaderCommentRule.kt index ae3ccc2390..6f4073e05b 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/HeaderCommentRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/HeaderCommentRule.kt @@ -77,7 +77,7 @@ class HeaderCommentRule : Rule("header-comment") { } if (isWrongCopyright || isMissingCopyright || isCopyrightInsideKdoc) { - HEADER_MISSING_OR_WRONG_COPYRIGHT.warnAndFix(configRules, emitWarn, isFixMode, fileName, node.startOffset) { + HEADER_MISSING_OR_WRONG_COPYRIGHT.warnAndFix(configRules, emitWarn, isFixMode, fileName, node.startOffset, node) { if (headerComment != null) { node.removeChild(headerComment) } @@ -107,7 +107,7 @@ class HeaderCommentRule : Rule("header-comment") { val hasOrphanedKdocAfterImports = firstKdoc != null && firstKdoc.treeParent.elementType == FILE if (configRules.isRuleEnabled(HEADER_NOT_BEFORE_PACKAGE) && node.findChildBefore(PACKAGE_DIRECTIVE, KDOC) == null && hasOrphanedKdocAfterImports) { - HEADER_NOT_BEFORE_PACKAGE.warnAndFix(configRules, emitWarn, isFixMode, fileName, firstKdoc!!.startOffset) { + HEADER_NOT_BEFORE_PACKAGE.warnAndFix(configRules, emitWarn, isFixMode, fileName, firstKdoc!!.startOffset, firstKdoc) { node.moveChildBefore(firstKdoc, node.getFirstChildWithType(PACKAGE_DIRECTIVE), true) // ensure there is no empty line between copyright and header kdoc node.findChildBefore(PACKAGE_DIRECTIVE, BLOCK_COMMENT)?.apply { @@ -128,7 +128,7 @@ class HeaderCommentRule : Rule("header-comment") { if (headerKdoc == null) { val nDeclaredClasses = node.getAllChildrenWithType(ElementType.CLASS).size if (nDeclaredClasses == 0 || nDeclaredClasses > 1) { - HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE.warn(configRules, emitWarn, isFixMode, fileName, node.startOffset) + HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE.warn(configRules, emitWarn, isFixMode, fileName, node.startOffset, node) } } else { // fixme we should also check date of creation, but it can be in different formats @@ -136,13 +136,13 @@ class HeaderCommentRule : Rule("header-comment") { .filter { it.contains("@author") } .forEach { HEADER_CONTAINS_DATE_OR_AUTHOR.warn(configRules, emitWarn, isFixMode, - it.trim(), headerKdoc.startOffset) + it.trim(), headerKdoc.startOffset, headerKdoc) } if (headerKdoc.treeNext != null && headerKdoc.treeNext.elementType == WHITE_SPACE && headerKdoc.treeNext.text.count { it == '\n' } != 2) { HEADER_WRONG_FORMAT.warnAndFix(configRules, emitWarn, isFixMode, - "header KDoc should have a new line after", headerKdoc.startOffset) { + "header KDoc should have a new line after", headerKdoc.startOffset, headerKdoc) { node.replaceChild(headerKdoc.treeNext, PsiWhiteSpaceImpl("\n\n")) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/BlankLinesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/BlankLinesRule.kt index 91b30a125b..e3b4795142 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/BlankLinesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/BlankLinesRule.kt @@ -50,7 +50,7 @@ class BlankLinesRule : Rule("blank-lines") { // if both are present, this is not beginning or end // if both are null, then this block is empty and is handled in another rule val freeText = "do not put newlines ${if (node.treePrev.elementType == LBRACE) "in the beginning" else "at the end"} of code blocks" - TOO_MANY_BLANK_LINES.warnAndFix(configRules, emitWarn, isFixMode, freeText, node.startOffset) { + TOO_MANY_BLANK_LINES.warnAndFix(configRules, emitWarn, isFixMode, freeText, node.startOffset, node) { node.leaveOnlyOneNewLine() } } @@ -58,7 +58,7 @@ class BlankLinesRule : Rule("blank-lines") { } private fun handleTooManyBlankLines(node: ASTNode) { - TOO_MANY_BLANK_LINES.warnAndFix(configRules, emitWarn, isFixMode, "do not use more than two consecutive blank lines", node.startOffset) { + TOO_MANY_BLANK_LINES.warnAndFix(configRules, emitWarn, isFixMode, "do not use more than two consecutive blank lines", node.startOffset, node) { node.leaveExactlyNumNewLines(2) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileSize.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileSize.kt index 33053a3076..eb8d3e7a33 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileSize.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileSize.kt @@ -65,7 +65,7 @@ class FileSize : Rule("file-size") { private fun checkFileSize(node: ASTNode, maxSize: Long) { val size = node.text.split("\n").size if (size > maxSize) { - FILE_IS_TOO_LONG.warn(configRules, emitWarn, isFixMode, "$size", node.startOffset) + FILE_IS_TOO_LONG.warn(configRules, emitWarn, isFixMode, "$size", node.startOffset, node) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt index 4385ff4f51..289f4e6381 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt @@ -65,7 +65,7 @@ class FileStructureRule : Rule("file-structure") { val codeTokens = TokenSet.andNot(TokenSet.ANY, TokenSet.create(WHITE_SPACE, KDOC, BLOCK_COMMENT, EOL_COMMENT, PACKAGE_DIRECTIVE, IMPORT_LIST)) val hasCode = node.getChildren(codeTokens).isNotEmpty() if (!hasCode) { - FILE_CONTAINS_ONLY_COMMENTS.warn(configRules, emitWarn, isFixMode, fileName, node.startOffset) + FILE_CONTAINS_ONLY_COMMENTS.warn(configRules, emitWarn, isFixMode, fileName, node.startOffset, node) } return hasCode } @@ -84,7 +84,7 @@ class FileStructureRule : Rule("file-structure") { listOfNotNull(copyrightComment, headerKdoc, fileAnnotations).handleIncorrectOrder({ getSiblingBlocks(copyrightComment, headerKdoc, fileAnnotations, packageDirectiveNode) }) { astNode, beforeThisNode -> - FILE_INCORRECT_BLOCKS_ORDER.warnAndFix(configRules, emitWarn, isFixMode, astNode.text.lines().first(), astNode.startOffset) { + FILE_INCORRECT_BLOCKS_ORDER.warnAndFix(configRules, emitWarn, isFixMode, astNode.text.lines().first(), astNode.startOffset, astNode) { node.moveChildBefore(astNode, beforeThisNode, true) astNode.treeNext?.let { node.replaceChild(it, PsiWhiteSpaceImpl("\n\n")) } } @@ -94,7 +94,7 @@ class FileStructureRule : Rule("file-structure") { arrayOf(copyrightComment, headerKdoc, fileAnnotations, packageDirectiveNode, importsList).forEach { astNode -> astNode?.treeNext?.apply { if (elementType == WHITE_SPACE && text.count { it == '\n' } != 2) { - FILE_NO_BLANK_LINE_BETWEEN_BLOCKS.warnAndFix(configRules, emitWarn, isFixMode, astNode.text.lines().first(), astNode.startOffset) { + FILE_NO_BLANK_LINE_BETWEEN_BLOCKS.warnAndFix(configRules, emitWarn, isFixMode, astNode.text.lines().first(), astNode.startOffset, astNode) { (this as LeafPsiElement).replaceWithText("\n\n${text.replace("\n", "")}") } } @@ -107,12 +107,12 @@ class FileStructureRule : Rule("file-structure") { // importPath can be null if import name cannot be parsed, which should be a very rare case, therefore !! should be safe here imports.filter { (it.psi as KtImportDirective).importPath!!.isAllUnder }.forEach { - FILE_WILDCARD_IMPORTS.warn(configRules, emitWarn, isFixMode, it.text, it.startOffset) + FILE_WILDCARD_IMPORTS.warn(configRules, emitWarn, isFixMode, it.text, it.startOffset, it) } val sortedImports = imports.sortedBy { it.text } if (sortedImports != imports) { - FILE_UNORDERED_IMPORTS.warnAndFix(configRules, emitWarn, isFixMode, fileName, node.startOffset) { + FILE_UNORDERED_IMPORTS.warnAndFix(configRules, emitWarn, isFixMode, fileName, node.startOffset, node) { rearrangeImports(node, imports, sortedImports) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/IndentationRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/IndentationRule.kt index 3b7cc5bd4c..5a48ff5041 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/IndentationRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/IndentationRule.kt @@ -97,7 +97,7 @@ class IndentationRule : Rule("indentation") { .filter { it.textContains('\t') } .apply { if (isEmpty()) return true } .forEach { - WRONG_INDENTATION.warnAndFix(configRules, emitWarn, isFixMode, "tabs are not allowed for indentation", it.startOffset + it.text.indexOf('\t')) { + WRONG_INDENTATION.warnAndFix(configRules, emitWarn, isFixMode, "tabs are not allowed for indentation", it.startOffset + it.text.indexOf('\t'), it) { (it as LeafPsiElement).replaceWithText(it.text.replace("\t", " ".repeat(INDENT_SIZE))) } } @@ -111,7 +111,7 @@ class IndentationRule : Rule("indentation") { if (configuration.newlineAtEnd) { val lastChild = node.lastChildNode if (lastChild.elementType != WHITE_SPACE || lastChild.text.count { it == '\n' } != 1) { - WRONG_INDENTATION.warnAndFix(configRules, emitWarn, isFixMode, "no newline at the end of file $fileName", node.startOffset + node.textLength) { + WRONG_INDENTATION.warnAndFix(configRules, emitWarn, isFixMode, "no newline at the end of file $fileName", node.startOffset + node.textLength, node) { if (lastChild.elementType != WHITE_SPACE) { node.addChild(PsiWhiteSpaceImpl("\n"), null) } else { @@ -165,7 +165,7 @@ class IndentationRule : Rule("indentation") { } if (checkResult?.isCorrect != true && expectedIndent != indentError.actual) { WRONG_INDENTATION.warnAndFix(configRules, emitWarn, isFixMode, "expected $expectedIndent but was ${indentError.actual}", - whiteSpace.startOffset + whiteSpace.text.lastIndexOf('\n') + 1) { + whiteSpace.startOffset + whiteSpace.text.lastIndexOf('\n') + 1, astNode) { whiteSpace.node.indentBy(expectedIndent) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt index fa81833c47..daa2b68bc6 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt @@ -119,7 +119,7 @@ class NewlinesRule : Rule("newlines") { private fun handleSemicolon(node: ASTNode) { if (node.isEol() && node.treeParent.elementType != ENUM_ENTRY) { // semicolon at the end of line which is not part of enum members declarations - REDUNDANT_SEMICOLON.warnAndFix(configRules, emitWarn, isFixMode, node.extractLineOfText(), node.startOffset) { + REDUNDANT_SEMICOLON.warnAndFix(configRules, emitWarn, isFixMode, node.extractLineOfText(), node.startOffset, node) { node.treeParent.removeChild(node) } } @@ -135,7 +135,7 @@ class NewlinesRule : Rule("newlines") { // at the beginning of the line. if (node.prevCodeSibling()?.isFollowedByNewline() == true) { WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, - "should break a line after and not before ${node.text}", node.startOffset) { + "should break a line after and not before ${node.text}", node.startOffset, node) { node.run { treeParent.removeChild(treePrev) if (!isFollowedByNewline()) { @@ -166,7 +166,7 @@ class NewlinesRule : Rule("newlines") { } else { "should break a line before and not after ${node.text}" } - WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, freeText, node.startOffset) { + WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, freeText, node.startOffset, node) { node.selfOrOperationReferenceParent().run { if (!isBeginByNewline()) { // prepend newline @@ -193,7 +193,7 @@ class NewlinesRule : Rule("newlines") { val isNotAnonymous = parent.treeParent.elementType in listOf(CALL_EXPRESSION, PRIMARY_CONSTRUCTOR, SECONDARY_CONSTRUCTOR, FUN) if (prevWhiteSpace != null && isNotAnonymous) { WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, - "opening parentheses should not be separated from constructor or function name", node.startOffset) { + "opening parentheses should not be separated from constructor or function name", node.startOffset, node) { prevWhiteSpace.treeParent.removeChild(prevWhiteSpace) } } @@ -208,7 +208,7 @@ class NewlinesRule : Rule("newlines") { it.elementType == WHITE_SPACE && it.text.contains("\n") } if (prevNewLine != null) { - WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, "newline should be placed only after comma", node.startOffset) { + WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, "newline should be placed only after comma", node.startOffset, node) { prevNewLine.treeParent.removeChild(prevNewLine) } } @@ -226,7 +226,7 @@ class NewlinesRule : Rule("newlines") { .toList() if (newlinesBeforeArrow.isNotEmpty() || !arrowNode.isFollowedByNewline()) { WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, - "in lambda with several lines in body newline should be placed after an arrow", arrowNode.startOffset) { + "in lambda with several lines in body newline should be placed after an arrow", arrowNode.startOffset, node) { // fixme: replacement logic can be sophisticated for better appearance? newlinesBeforeArrow.forEach { it.treeParent.replaceChild(it, PsiWhiteSpaceImpl(" ")) } arrowNode.treeNext.takeIf { it.elementType == WHITE_SPACE }?.leaveOnlyOneNewLine() @@ -237,7 +237,7 @@ class NewlinesRule : Rule("newlines") { val lbraceNode = node.treeParent.firstChildNode if (!lbraceNode.isFollowedByNewline()) { WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, - "in lambda with several lines in body newline should be placed after an opening brace", lbraceNode.startOffset) { + "in lambda with several lines in body newline should be placed after an opening brace", lbraceNode.startOffset, node) { lbraceNode.treeNext.let { if (it.elementType == WHITE_SPACE) { it.leaveOnlyOneNewLine() @@ -265,7 +265,7 @@ class NewlinesRule : Rule("newlines") { .takeIf { it.size == 1 } ?.also { WRONG_NEWLINES.warnAndFix(configRules, emitWarn, isFixMode, - "functions with single return statement should be simplified to expression body", node.startOffset) { + "functions with single return statement should be simplified to expression body", node.startOffset, node) { val funNode = blockNode.treeParent // if return type is not Unit, then there should be type specification // otherwise code won't compile and colon being null is correctly invalid diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocComments.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocComments.kt index 244a553209..ad5ff8d684 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocComments.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocComments.kt @@ -79,7 +79,7 @@ class KdocComments : Rule("kdoc-comments") { val name = node.getIdentifierName() if (modifier.isAccessibleOutside() && kDoc == null) { - warning.warn(configRules, emitWarn, isFixMode, name!!.text, node.startOffset) + warning.warn(configRules, emitWarn, isFixMode, name!!.text, node.startOffset,node) } } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocFormatting.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocFormatting.kt index 7b3fc0754c..fead8f85f9 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocFormatting.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocFormatting.kt @@ -97,7 +97,7 @@ class KdocFormatting : Rule("kdoc-formatting") { val nodeAfterKdoc = kdoc?.treeNext val name = node.getFirstChildWithType(ElementType.IDENTIFIER) if (nodeAfterKdoc?.elementType == WHITE_SPACE && nodeAfterKdoc.text.countSubStringOccurrences("\n") > 1) { - BLANK_LINE_AFTER_KDOC.warnAndFix(configRules, emitWarn, isFixMode, name!!.text, nodeAfterKdoc.startOffset) { + BLANK_LINE_AFTER_KDOC.warnAndFix(configRules, emitWarn, isFixMode, name!!.text, nodeAfterKdoc.startOffset, nodeAfterKdoc) { nodeAfterKdoc.leaveOnlyOneNewLine() } } @@ -112,7 +112,7 @@ class KdocFormatting : Rule("kdoc-formatting") { KDOC_EMPTY_KDOC.warn(configRules, emitWarn, isFixMode, node.treeParent.getIdentifierName()?.text ?: node.nextSibling { it.elementType in KtTokens.KEYWORDS }?.text - ?: fileName, node.startOffset) + ?: fileName, node.startOffset, node) } return isKdocNotEmpty } @@ -121,7 +121,7 @@ class KdocFormatting : Rule("kdoc-formatting") { val kDocTags = node.kDocTags() kDocTags?.find { it.name == "deprecated" } ?.let { kDocTag -> - KDOC_NO_DEPRECATED_TAG.warnAndFix(configRules, emitWarn, isFixMode, kDocTag.text, kDocTag.node.startOffset) { + KDOC_NO_DEPRECATED_TAG.warnAndFix(configRules, emitWarn, isFixMode, kDocTag.text, kDocTag.node.startOffset, kDocTag.node) { val kDocSection = kDocTag.node.treeParent val deprecatedTagNode = kDocSection.getChildren(TokenSet.create(KDOC_TAG)) .find { "@deprecated" in it.text }!! @@ -140,7 +140,7 @@ class KdocFormatting : Rule("kdoc-formatting") { kDocTags?.filter { it.getSubjectName() == null && it.getContent().isEmpty() }?.forEach { - KDOC_NO_EMPTY_TAGS.warn(configRules, emitWarn, isFixMode, "@${it.name!!}", it.node.startOffset) + KDOC_NO_EMPTY_TAGS.warn(configRules, emitWarn, isFixMode, "@${it.name!!}", it.node.startOffset, it.node) } } @@ -155,7 +155,7 @@ class KdocFormatting : Rule("kdoc-formatting") { || tag.node.findChildAfter(KDOC_TAG_NAME, WHITE_SPACE)?.text != " " }?.forEach { tag -> KDOC_WRONG_SPACES_AFTER_TAG.warnAndFix(configRules, emitWarn, isFixMode, - "@${tag.name!!}", tag.node.startOffset) { + "@${tag.name!!}", tag.node.startOffset, tag.node) { tag.node.findChildBefore(KDOC_TEXT, WHITE_SPACE) ?.let { tag.node.replaceChild(it, LeafPsiElement(WHITE_SPACE, " ")) } tag.node.findChildAfter(KDOC_TAG_NAME, WHITE_SPACE) @@ -182,7 +182,7 @@ class KdocFormatting : Rule("kdoc-formatting") { if (kDocTags != null && !isTagsInCorrectOrder!!) { KDOC_WRONG_TAGS_ORDER.warnAndFix(configRules, emitWarn, isFixMode, - basicTags.joinToString(", ") { "@${it.name}" }, basicTags.first().node.startOffset) { + basicTags.joinToString(", ") { "@${it.name}" }, basicTags.first().node.startOffset, node) { val kDocSection = node.getFirstChildWithType(KDOC_SECTION)!! val basicTagChildren = kDocTags .filter { basicTagsOrdered.contains(it.knownTag) } @@ -212,7 +212,7 @@ class KdocFormatting : Rule("kdoc-formatting") { if (hasContentBefore xor hasEmptyLineBefore) { KDOC_NEWLINES_BEFORE_BASIC_TAGS.warnAndFix(configRules, emitWarn, isFixMode, - "@${firstBasicTag.name!!}", firstBasicTag.node.startOffset) { + "@${firstBasicTag.name!!}", firstBasicTag.node.startOffset, firstBasicTag.node) { if (hasContentBefore) { if (previousTag != null) { previousTag.applyToPrevSibling(KDOC_LEADING_ASTERISK) { @@ -248,7 +248,7 @@ class KdocFormatting : Rule("kdoc-formatting") { tagsWithRedundantEmptyLines.forEach { tag -> KDOC_NO_NEWLINES_BETWEEN_BASIC_TAGS.warnAndFix(configRules, emitWarn, isFixMode, - "@${tag.name}", tag.startOffset) { + "@${tag.name}", tag.startOffset, tag.node) { tag.node.nextSibling { it.elementType == WHITE_SPACE }?.leaveOnlyOneNewLine() // the first asterisk before tag is not included inside KDOC_TAG node // we look for the second and take its previous which should be WHITE_SPACE with newline @@ -276,7 +276,7 @@ class KdocFormatting : Rule("kdoc-formatting") { if (presentSpecialTagNodes != null && poorlyFormattedTagNodes!!.isNotEmpty()) { KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS.warnAndFix(configRules, emitWarn, isFixMode, poorlyFormattedTagNodes.joinToString(", ") { "@${(it.psi as KDocTag).name!!}" }, - poorlyFormattedTagNodes.first().startOffset) { + poorlyFormattedTagNodes.first().startOffset, node) { poorlyFormattedTagNodes.forEach { node -> while (node.lastChildNode.elementType == KDOC_LEADING_ASTERISK && node.lastChildNode.treePrev.treePrev.elementType == KDOC_LEADING_ASTERISK) { diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocMethods.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocMethods.kt index f2c87a9a9a..30dd5247c8 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocMethods.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocMethods.kt @@ -106,7 +106,7 @@ class KdocMethods : Rule("kdoc-methods") { if (kDoc == null && anyTagFailed) { addKdocTemplate(node, name, missingParameters, explicitlyThrownExceptions, returnCheckFailed) } else if (kDoc == null) { - MISSING_KDOC_ON_FUNCTION.warn(configRules, emitWarn, false, name, node.startOffset) + MISSING_KDOC_ON_FUNCTION.warn(configRules, emitWarn, false, name, node.startOffset, node) } } @@ -151,7 +151,7 @@ class KdocMethods : Rule("kdoc-methods") { missingParameters: Collection, kDocTags: Collection?) { KDOC_WITHOUT_PARAM_TAG.warnAndFix(configRules, emitWarn, isFixMode, - "$name (${missingParameters.joinToString()})", node.startOffset) { + "$name (${missingParameters.joinToString()})", node.startOffset, node) { val beforeTag = kDocTags?.find { it.knownTag == KDocKnownTag.RETURN } ?: kDocTags?.find { it.knownTag == KDocKnownTag.THROWS } missingParameters.forEach { @@ -169,7 +169,7 @@ class KdocMethods : Rule("kdoc-methods") { kDoc: ASTNode?, kDocTags: Collection? ) { - KDOC_WITHOUT_RETURN_TAG.warnAndFix(configRules, emitWarn, isFixMode, name, node.startOffset) { + KDOC_WITHOUT_RETURN_TAG.warnAndFix(configRules, emitWarn, isFixMode, name, node.startOffset, node) { val beforeTag = kDocTags?.find { it.knownTag == KDocKnownTag.THROWS } kDoc?.insertTagBefore(beforeTag?.node) { addChild(LeafPsiElement(KDOC_TAG_NAME, "@return")) @@ -183,7 +183,7 @@ class KdocMethods : Rule("kdoc-methods") { missingExceptions: Collection ) { KDOC_WITHOUT_THROWS_TAG.warnAndFix(configRules, emitWarn, isFixMode, - "$name (${missingExceptions.joinToString()})", node.startOffset) { + "$name (${missingExceptions.joinToString()})", node.startOffset, node) { missingExceptions.forEach { kDoc?.insertTagBefore(null) { addChild(LeafPsiElement(KDOC_TAG_NAME, "@throws")) @@ -200,7 +200,7 @@ class KdocMethods : Rule("kdoc-methods") { explicitlyThrownExceptions: Collection, returnCheckFailed: Boolean ) { - MISSING_KDOC_ON_FUNCTION.warnAndFix(configRules, emitWarn, isFixMode, name, node.startOffset) { + MISSING_KDOC_ON_FUNCTION.warnAndFix(configRules, emitWarn, isFixMode, name, node.startOffset, node) { val kDocTemplate = "/**\n" + (missingParameters.joinToString("") { " * @param $it\n" } + (if (returnCheckFailed) " * @return\n" else "") + @@ -218,7 +218,7 @@ class KdocMethods : Rule("kdoc-methods") { if (kdocTextNodes.size == 1) { val kdocText = kdocTextNodes.first().text.trim() if (kdocText.matches(uselessKdocRegex)) { - KDOC_TRIVIAL_KDOC_ON_FUNCTION.warn(configRules, emitWarn, isFixMode, kdocText, kdocTextNodes.first().startOffset) + KDOC_TRIVIAL_KDOC_ON_FUNCTION.warn(configRules, emitWarn, isFixMode, kdocText, kdocTextNodes.first().startOffset, node) } } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index c39df3a897..7ab526403c 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -293,11 +293,16 @@ fun ASTNode?.isAccessibleOutside(): Boolean = true } -fun ASTNode.hasSuppress(warningName: String): Boolean = parent(ANNOTATION) - .let { node -> - node?.getAllChildrenWithType(ANNOTATION_ENTRY)!!.any { it.text.contains("Suppress") - && it.text.contains(warningName) } +fun ASTNode.hasSuppress(warningName: String): Boolean { + if (elementType == FILE) { + return findAllNodesWithSpecificType(ANNOTATION_ENTRY).any { + it.text.contains("Suppress") && it.text.contains(warningName) } + } + return parent({ it.findAllNodesWithSpecificType(ANNOTATION_ENTRY).any { + it.text.contains("Suppress") && it.text.contains(warningName) + }}) != null +} /** * removing all newlines in WHITE_SPACE node and replacing it to a one newline saving the initial indenting format */ diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt new file mode 100644 index 0000000000..bd573d2789 --- /dev/null +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt @@ -0,0 +1,22 @@ +package org.cqfn.diktat.ruleset.utils + +import org.cqfn.diktat.ruleset.rules.IdentifierNaming +import org.cqfn.diktat.util.lintMethod +import org.junit.jupiter.api.Test + +class SuppressTest { + + @Test + fun `test suppress`() { + val code = + """ + @Suppress("FUNCTION_NAME_INCORRECT_CASE") + class SomeClass { + fun /* */ methODTREE(): String { + + } + } + """.trimIndent() + lintMethod(IdentifierNaming(), code) + } +} \ No newline at end of file From a896a3c1c5541928a5590bf46560da2a1e249aca Mon Sep 17 00:00:00 2001 From: aktsay Date: Tue, 8 Sep 2020 17:15:51 +0300 Subject: [PATCH 04/20] feature/suppress-in-individual-files ### What's done: * Fixed bugs --- .../main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt | 2 ++ .../src/main/kotlin/org/cqfn/diktat/ruleset/rules/LineLength.kt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt index ccc02b9a3e..c6978b41ea 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt @@ -3,6 +3,8 @@ package org.cqfn.diktat.ruleset.constants import org.cqfn.diktat.common.config.rules.Rule import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.common.config.rules.isRuleEnabled +import org.cqfn.diktat.ruleset.utils.hasSuppress +import org.jetbrains.kotlin.com.intellij.lang.ASTNode @Suppress("ForbiddenComment", "MagicNumber") enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: String) : Rule { diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LineLength.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LineLength.kt index a0ac67272f..f395357772 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LineLength.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LineLength.kt @@ -104,7 +104,7 @@ class LineLength(private val configRules: List) : Rule("line-length val fixableType = isFixable(newNode, configuration) LONG_LINE.warnAndFix(configRules, emitWarn, isFixMode, "max line length ${configuration.lineLength}, but was ${it.length}", - offset + node.startOffset, fixableType != LongLineFixableCases.None, node) { + offset + node.startOffset, node, fixableType != LongLineFixableCases.None) { fixError(fixableType) } } From 24bd86fc12fdaec0c6db30c84deeaa4246922f4b Mon Sep 17 00:00:00 2001 From: aktsay Date: Tue, 8 Sep 2020 17:54:09 +0300 Subject: [PATCH 05/20] feature/suppress-in-individual-files ### What's done: * Fixed bugs --- .../cqfn/diktat/ruleset/constants/Warnings.kt | 5 +++++ .../ruleset/rules/AnnotationNewLineRule.kt | 2 +- .../rules/LongNumericalValuesSeparatedRule.kt | 6 ++--- .../rules/MultipleModifiersSequence.kt | 2 +- .../org/cqfn/diktat/ruleset/rules/SortRule.kt | 4 ++-- .../ruleset/rules/WhenMustHaveElseRule.kt | 2 +- .../ruleset/rules/comments/CommentsRule.kt | 1 - .../diktat/ruleset/chapter3/SuppressTest.kt | 22 +++++++++++++++++++ .../cqfn/diktat/ruleset/utils/SuppressTest.kt | 6 ++--- 9 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/SuppressTest.kt diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt index c6978b41ea..080141f28b 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt @@ -79,7 +79,12 @@ enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: S TOO_MANY_BLANK_LINES(true, "too many consecutive blank lines"), WRONG_WHITESPACE(true, "incorrect usage of whitespaces for code separation"), TOO_MANY_CONSECUTIVE_SPACES(true, "too many consecutive spaces"), + ANNOTATION_NEW_LINE(true, "annotations must be on new line"), ENUMS_SEPARATED(true, "split enumeration error"), + WHEN_WITHOUT_ELSE(true, "each when statement must have else at the end"), + LONG_NUMERICAL_VALUES_SEPARATED(true, "long numerical values should be separated with underscore"), + WRONG_DECLARATIONS_ORDER(true, "declarations of constants and enum members should be sorted alphabetically"), + WRONG_MULTIPLE_MODIFIERS_ORDER(true, "sequence of modifiers is incorrect"), ; override fun ruleName(): String = this.name diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt index 66512653b7..fc0eb6a0d8 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt @@ -59,7 +59,7 @@ class AnnotationNewLineRule(private val configRules: List) : Rule(" private fun deleteSpaces(node: ASTNode, rightSide: Boolean, leftSide: Boolean) { Warnings.ANNOTATION_NEW_LINE.warnAndFix(configRules, emitWarn, isFixMode, "${node.text} not on a single line", - node.startOffset) { + node.startOffset, node) { if (rightSide) { if (node.treeNext?.isWhiteSpace() == true) { node.removeChild(node.treeNext) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LongNumericalValuesSeparatedRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LongNumericalValuesSeparatedRule.kt index e8fd5ddaa6..bb2330cad1 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LongNumericalValuesSeparatedRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LongNumericalValuesSeparatedRule.kt @@ -30,7 +30,7 @@ class LongNumericalValuesSeparatedRule(private val configRules: List configuration.maxBlockLength) { - Warnings.LONG_NUMERICAL_VALUES_SEPARATED.warn(configRules, emitWarn, false, "this block is too long $it", node.startOffset) + Warnings.LONG_NUMERICAL_VALUES_SEPARATED.warn(configRules, emitWarn, false, "this block is too long $it", node.startOffset, node) } } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/MultipleModifiersSequence.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/MultipleModifiersSequence.kt index 007a0734cb..c0fcf3116c 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/MultipleModifiersSequence.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/MultipleModifiersSequence.kt @@ -42,7 +42,7 @@ class MultipleModifiersSequence(private val configRules: List) : Ru if (modifierNode != sortModifierListOfPair[index]) { WRONG_MULTIPLE_MODIFIERS_ORDER.warnAndFix(configRules, emitWarn, isFixMode, "${modifierNode.text} should be on position ${sortModifierListOfPair.indexOf(modifierNode) + 1}, but is on position ${index + 1}", - modifierNode.startOffset) { + modifierNode.startOffset, modifierNode) { val nodeBefore = modifierNode.treeNext node.removeChild(modifierNode) node.addChild((sortModifierListOfPair[index].clone() as ASTNode), nodeBefore) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SortRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SortRule.kt index 2b2a121ec0..15c6a6f7c1 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SortRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SortRule.kt @@ -59,7 +59,7 @@ class SortRule(private val configRules: List) : Rule("sort-rule") { if (mutableList != sortedListOfList[index]) { WRONG_DECLARATIONS_ORDER.warnAndFix(configRules, emitWarn, isFixMode, "constant properties inside companion object order is incorrect", - mutableList.first().startOffset) { + mutableList.first().startOffset, node) { swapSortNodes(sortedListOfList[index], mutableList, node) } } @@ -97,7 +97,7 @@ class SortRule(private val configRules: List) : Rule("sort-rule") { return val sortList = enumEntryList.sortedBy { it.findChildByType(IDENTIFIER)!!.text } if (enumEntryList != sortList) { - WRONG_DECLARATIONS_ORDER.warnAndFix(configRules, emitWarn, isFixMode, "enum entries order is incorrect", node.startOffset) { + WRONG_DECLARATIONS_ORDER.warnAndFix(configRules, emitWarn, isFixMode, "enum entries order is incorrect", node.startOffset, node) { val (isEndSemicolon, isEndSpace) = removeLastSemicolonAndSpace(enumEntryList.last()) val hasTrailingComma = (sortList.last() != enumEntryList.last() && enumEntryList.last().hasChildOfType(COMMA)) swapSortNodes(sortList, enumEntryList, node) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/WhenMustHaveElseRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/WhenMustHaveElseRule.kt index 1bfc46687b..35dec76581 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/WhenMustHaveElseRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/WhenMustHaveElseRule.kt @@ -56,7 +56,7 @@ class WhenMustHaveElseRule(private val configRules: List) : Rule("n private fun checkEntries(node: ASTNode) { if (!hasElse(node)) { - Warnings.WHEN_WITHOUT_ELSE.warnAndFix(configRules, emitWarn, isFixMode, "else was not found", node.startOffset) { + Warnings.WHEN_WITHOUT_ELSE.warnAndFix(configRules, emitWarn, isFixMode, "else was not found", node.startOffset, node) { val whenEntryElse = CompositeElement(WHEN_ENTRY) node.appendNewlineMergingWhiteSpace(node.lastChildNode.treePrev, node.lastChildNode.treePrev) node.addChild(whenEntryElse, node.lastChildNode) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt index 2b22d92c44..a6ac2cb807 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt @@ -9,7 +9,6 @@ import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE import com.pinterest.ktlint.core.ast.prevSibling import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.ruleset.constants.Warnings.COMMENTED_OUT_CODE -import org.cqfn.diktat.ruleset.rules.getDiktatConfigRules import org.cqfn.diktat.ruleset.utils.findAllNodesWithSpecificType import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.TokenType diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/SuppressTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/SuppressTest.kt new file mode 100644 index 0000000000..b3e774e83f --- /dev/null +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/SuppressTest.kt @@ -0,0 +1,22 @@ +package org.cqfn.diktat.ruleset.chapter3 + +import org.cqfn.diktat.ruleset.rules.IdentifierNaming +import org.cqfn.diktat.util.LintTestBase +import org.junit.jupiter.api.Test + +class SuppressTest : LintTestBase(::IdentifierNaming) { + + @Test + fun `test suppress`() { + val code = + """ + @Suppress("FUNCTION_NAME_INCORRECT_CASE") + class SomeClass { + fun /* */ methODTREE(): String { + + } + } + """.trimIndent() + lintMethod(code) + } +} diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt index bd573d2789..255ff27db9 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt @@ -1,10 +1,10 @@ package org.cqfn.diktat.ruleset.utils import org.cqfn.diktat.ruleset.rules.IdentifierNaming -import org.cqfn.diktat.util.lintMethod +import org.cqfn.diktat.util.LintTestBase import org.junit.jupiter.api.Test -class SuppressTest { +class SuppressTest : LintTestBase(::IdentifierNaming) { @Test fun `test suppress`() { @@ -17,6 +17,6 @@ class SuppressTest { } } """.trimIndent() - lintMethod(IdentifierNaming(), code) + lintMethod(code) } } \ No newline at end of file From eb027bcbe03d9afc1273536e6ebfb26160f89f10 Mon Sep 17 00:00:00 2001 From: aktsay Date: Tue, 8 Sep 2020 17:56:27 +0300 Subject: [PATCH 06/20] feature/suppress-in-individual-files ### What's done: * Fixed bugs --- .../main/kotlin/org/cqfn/diktat/ruleset/rules/PackageNaming.kt | 3 ++- .../test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/PackageNaming.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/PackageNaming.kt index 7e3b54d08e..a578366e4c 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/PackageNaming.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/PackageNaming.kt @@ -243,7 +243,8 @@ class PackageNaming(private val configRules: List) : Rule("package- */ private fun checkFilePathMatchesWithPackageName(packageNameParts: List, realName: List) { if (realName.isNotEmpty() && packageNameParts.map { node -> node.text } != realName) { - PACKAGE_NAME_INCORRECT_PATH.warnAndFix(configRules, emitWarn, isFixMode, realName.joinToString(PACKAGE_SEPARATOR), packageNameParts[0].startOffset, packageNameParts[0]) { + PACKAGE_NAME_INCORRECT_PATH.warnAndFix(configRules, emitWarn, isFixMode, realName.joinToString(PACKAGE_SEPARATOR), + packageNameParts[0].startOffset, packageNameParts[0]) { // need to get first top-level DOT-QUALIFIED-EXPRESSION // -- PACKAGE_DIRECTIVE // -- DOT_QUALIFIED_EXPRESSION diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt index 255ff27db9..278fb0d411 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt @@ -19,4 +19,4 @@ class SuppressTest : LintTestBase(::IdentifierNaming) { """.trimIndent() lintMethod(code) } -} \ No newline at end of file +} From 601200c1a41a11ea9a4f521b9f13d2ca637dee40 Mon Sep 17 00:00:00 2001 From: aktsay Date: Tue, 8 Sep 2020 18:17:25 +0300 Subject: [PATCH 07/20] feature/suppress-in-individual-files ### What's done: * Fixed bugs --- .../main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt index 080141f28b..d39c06b779 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt @@ -106,6 +106,7 @@ enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: S fix(configRules, autoFix, isFixMode) } + @Suppress("LongParameterList") fun warn(configs: List, emit: ((offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit), autoCorrected: Boolean, From 2ef5b6920a364e718d1469a57a950e7b69d30839 Mon Sep 17 00:00:00 2001 From: aktsay Date: Thu, 10 Sep 2020 16:52:49 +0300 Subject: [PATCH 08/20] feature/suppress-in-individual-files ### What's done: * Fixed bugs --- .../cqfn/diktat/ruleset/constants/Warnings.kt | 2 +- .../diktat/ruleset/rules/kdoc/KdocMethods.kt | 3 ++- .../cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 4 ++-- .../diktat/ruleset/chapter3/SuppressTest.kt | 22 ------------------- 4 files changed, 5 insertions(+), 26 deletions(-) delete mode 100644 diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/SuppressTest.kt diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt index d39c06b779..42399c0818 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt @@ -14,7 +14,7 @@ enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: S PACKAGE_NAME_INCORRECT_PREFIX(true, "package name should start from company's domain"), // FixMe: should add autofix PACKAGE_NAME_INCORRECT_SYMBOLS(false, "package name should contain only latin (ASCII) letters or numbers. For separation of words use dot"), - PACKAGE_NAME_INCORRECT_PATH(true, "package name does not match the directory hierarchy for this file, the real package name should be:"), + PACKAGE_NAME_INCORRECT_PATH(true, "package name does not match the directory hierarchy for this file, the real package name should be"), INCORRECT_PACKAGE_SEPARATOR(true, "package name parts should be separated only by dots - there should be no other symbols like underscores (_)"), CLASS_NAME_INCORRECT(true, "class/enum/interface name should be in PascalCase and should contain only latin (ASCII) letters or numbers"), OBJECT_NAME_INCORRECT(true, "object structure name should be in PascalCase and should contain only latin (ASCII) letters or numbers"), diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocMethods.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocMethods.kt index 657707919f..026f62d3b7 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocMethods.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocMethods.kt @@ -148,7 +148,8 @@ class KdocMethods(private val configRules: List) : Rule("kdoc-metho kDocMissingParameters.forEach { KDOC_WITHOUT_PARAM_TAG.warn(configRules, emitWarn, false, - "${it.getSubjectName()} param isn't present in argument list", it.node.startOffset, it) + "${it.getSubjectName()} param isn't present in argument list", it.node.startOffset, + it.node) } if (missingParameters.isNotEmpty()) { KDOC_WITHOUT_PARAM_TAG.warnAndFix(configRules, emitWarn, isFixMode, diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index 2748632940..ee766ed674 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -306,11 +306,11 @@ fun ASTNode?.isAccessibleOutside(): Boolean = fun ASTNode.hasSuppress(warningName: String): Boolean { if (elementType == FILE) { return findAllNodesWithSpecificType(ANNOTATION_ENTRY).any { - it.text.contains("Suppress") && it.text.contains(warningName) + it.text.contains("Suppress($warningName)") } } return parent({ it.findAllNodesWithSpecificType(ANNOTATION_ENTRY).any { - it.text.contains("Suppress") && it.text.contains(warningName) + it.text.contains("Suppress($warningName)") }}) != null } /** diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/SuppressTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/SuppressTest.kt deleted file mode 100644 index b3e774e83f..0000000000 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/SuppressTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.cqfn.diktat.ruleset.chapter3 - -import org.cqfn.diktat.ruleset.rules.IdentifierNaming -import org.cqfn.diktat.util.LintTestBase -import org.junit.jupiter.api.Test - -class SuppressTest : LintTestBase(::IdentifierNaming) { - - @Test - fun `test suppress`() { - val code = - """ - @Suppress("FUNCTION_NAME_INCORRECT_CASE") - class SomeClass { - fun /* */ methODTREE(): String { - - } - } - """.trimIndent() - lintMethod(code) - } -} From 15bb1cce4ef12a04c24e3674887d17c02785086a Mon Sep 17 00:00:00 2001 From: aktsay Date: Thu, 10 Sep 2020 18:57:25 +0300 Subject: [PATCH 09/20] feature/suppress-in-individual-files ### What's done: * Fixed bugs --- .../diktat/ruleset/rules/LongNumericalValuesSeparatedRule.kt | 4 ++-- .../org/cqfn/diktat/ruleset/rules/comments/CommentsRule.kt | 2 +- .../org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt | 2 +- .../kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocComments.kt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LongNumericalValuesSeparatedRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LongNumericalValuesSeparatedRule.kt index d1d6ea0148..a08afad051 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LongNumericalValuesSeparatedRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/LongNumericalValuesSeparatedRule.kt @@ -31,7 +31,7 @@ class LongNumericalValuesSeparatedRule(private val configRules: List) : Rule("comments" .findAllNodesWithSpecificType(TokenType.ERROR_ELEMENT) .isEmpty() }.forEach { (offset, parsedNode) -> - COMMENTED_OUT_CODE.warn(configRules, emitWarn, isFixMode, parsedNode.text.substringBefore("\n").trim(), offset) + COMMENTED_OUT_CODE.warn(configRules, emitWarn, isFixMode, parsedNode.text.substringBefore("\n").trim(), offset, parsedNode) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt index 93b6e2e527..ec8b4ef15a 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt @@ -107,7 +107,7 @@ class FileStructureRule(private val configRules: List) : Rule("file // importPath can be null if import name cannot be parsed, which should be a very rare case, therefore !! should be safe here - imports.filter { (it.psi as KtImportDirective).importPath!!.isAllUnder }.forEach { + imports.filter { (it.psi as KtImportDirective).importPath!!.isAllUnder && it.text !in configuration.allowedWildcards }.forEach { FILE_WILDCARD_IMPORTS.warn(configRules, emitWarn, isFixMode, it.text, it.startOffset, it) } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocComments.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocComments.kt index c4ee85a5ce..7c94529cef 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocComments.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocComments.kt @@ -74,7 +74,7 @@ class KdocComments(private val configRules: List) : Rule("kdoc-comm val modifier = node.getFirstChildWithType(MODIFIER_LIST) val name = node.getIdentifierName() - if (modifier.isAccessibleOutside() && kDoc == null) { + if (modifier.isAccessibleOutside() && kdoc == null) { warning.warn(configRules, emitWarn, isFixMode, name!!.text, node.startOffset,node) } } From 325bb9204f683962e45739a1c9b1facb886f1fb7 Mon Sep 17 00:00:00 2001 From: aktsay Date: Thu, 10 Sep 2020 19:03:41 +0300 Subject: [PATCH 10/20] feature/suppress-in-individual-files ### What's done: * Fixed bugs --- .../kotlin/org/cqfn/diktat/ruleset/rules/EmptyBlock.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EmptyBlock.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EmptyBlock.kt index 696b9b19c6..07885cd0e4 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EmptyBlock.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/EmptyBlock.kt @@ -40,17 +40,19 @@ class EmptyBlock(private val configRules: List) : Rule("empty-block if (node.treeParent.findChildByType(MODIFIER_LIST)?.findChildByType(OVERRIDE_KEYWORD) != null) return if (node.isBlockEmpty()) { if (!configuration.emptyBlockExist) { - EMPTY_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "empty blocks are forbidden unless it is function with override keyword", - node.startOffset, node) {} + EMPTY_BLOCK_STRUCTURE_ERROR.warn(configRules, emitWarn, isFixMode, "empty blocks are forbidden unless it is function with override keyword", + node.startOffset, node) } else { val space = node.findChildByType(RBRACE)!!.treePrev if (configuration.emptyBlockNewline && !space.text.contains("\n")) { EMPTY_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "different style for empty block", node.startOffset, node) { - if (space.elementType == WHITE_SPACE) + if (space.elementType == WHITE_SPACE) { (space.treeNext as LeafPsiElement).replaceWithText("\n") - else + } + else { node.addChild(PsiWhiteSpaceImpl("\n"), space.treeNext) + } } } else if (!configuration.emptyBlockNewline && space.text.contains("\n")) { EMPTY_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "different style for empty block", From 9a3f9d3e627c753039d45dc8da6c76e51b8fc65d Mon Sep 17 00:00:00 2001 From: soWhoAmI Date: Fri, 11 Sep 2020 12:20:20 +0300 Subject: [PATCH 11/20] feature/suppress-in-individual-files ### What's done: * Fixed bugs --- .../cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 21 +++++--- .../cqfn/diktat/ruleset/utils/SuppressTest.kt | 49 ++++++++++++++++++- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index 939962283c..32c6b4e34f 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -23,6 +23,7 @@ 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.IElementType import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet +import org.jetbrains.kotlin.psi.KtAnnotationEntry import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.psiUtil.parents @@ -304,15 +305,19 @@ fun ASTNode?.isAccessibleOutside(): Boolean = } fun ASTNode.hasSuppress(warningName: String): Boolean { - return if (elementType == FILE) { - findAllNodesWithSpecificType(ANNOTATION_ENTRY).any { - it.text.contains("Suppress(\"$warningName\")") - } + return if (findChildByType(MODIFIER_LIST) != null) { + findChildByType(MODIFIER_LIST)?.findAllNodesWithSpecificType(ANNOTATION_ENTRY)?.any { + (it.psi as KtAnnotationEntry).shortName.toString() == "Suppress" + && (it.psi).text.contains(warningName) + } ?: false } else { - parent({ - it.findAllNodesWithSpecificType(ANNOTATION_ENTRY).any { - it.text.contains("Suppress(\"$warningName\")") - } + parent({ node -> + node.findChildByType(MODIFIER_LIST) + ?.findAllNodesWithSpecificType(ANNOTATION_ENTRY) + ?.any { + (it.psi as KtAnnotationEntry).shortName.toString() == "Suppress" + && (it.psi).text.contains(warningName) + } ?: false }) != null } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt index 278fb0d411..c044e80e5b 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt @@ -1,13 +1,18 @@ package org.cqfn.diktat.ruleset.utils +import com.pinterest.ktlint.core.LintError +import org.cqfn.diktat.ruleset.constants.Warnings +import org.cqfn.diktat.ruleset.rules.DIKTAT_RULE_SET_ID import org.cqfn.diktat.ruleset.rules.IdentifierNaming import org.cqfn.diktat.util.LintTestBase import org.junit.jupiter.api.Test class SuppressTest : LintTestBase(::IdentifierNaming) { + private val ruleId: String = "$DIKTAT_RULE_SET_ID:identifier-naming" + @Test - fun `test suppress`() { + fun `test suppress on class`() { val code = """ @Suppress("FUNCTION_NAME_INCORRECT_CASE") @@ -19,4 +24,46 @@ class SuppressTest : LintTestBase(::IdentifierNaming) { """.trimIndent() lintMethod(code) } + + @Test + fun `check suppress on method`() { + lintMethod( + """ + |class SomeClass { + | + | @Suppress("FUNCTION_NAME_INCORRECT_CASE") + | fun /* */ methODTREE(): String { + | + | fun soMEMETHOD() { + | + | } + | + | } + | + | fun /* */ methODTREEASA(): String { + | + | } + |} + """.trimMargin(), + LintError(12,14,ruleId, "${Warnings.FUNCTION_NAME_INCORRECT_CASE.warnText()} methODTREEASA", + true) + ) + } + + @Test + fun `check suppress on variable`() { + lintMethod( + """ + |class SomeClass { + | + | @Suppress("FUNCTION_NAME_INCORRECT_CASE") + | fun /* */ methODTREE(): String { + | @Suppress( "VARIABLE_NAME_INCORRECT_FORMAT" ) + | var SOMEvar = 5 + | } + |} + """.trimMargin() + ) + } + } From 81f0a33dc633978fcb48a08b93e52314ac09822a Mon Sep 17 00:00:00 2001 From: soWhoAmI Date: Fri, 11 Sep 2020 12:28:07 +0300 Subject: [PATCH 12/20] feature/suppress-in-individual-files ### What's done: * Fixed bugs --- .../kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt index 9cd5dc95aa..2583d3bf30 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt @@ -196,7 +196,7 @@ class IdentifierNaming(private val configRules: List) : Rule("ident else -> "" } - CONFUSING_IDENTIFIER_NAMING.warn(configRules, emitWarn, false, warnText, variableName.startOffset) + CONFUSING_IDENTIFIER_NAMING.warn(configRules, emitWarn, false, warnText, variableName.startOffset, variableName) } /** From 3d4b8f8e9ac3f39919d9ae48fe8f447681f7c326 Mon Sep 17 00:00:00 2001 From: soWhoAmI Date: Mon, 14 Sep 2020 12:18:21 +0300 Subject: [PATCH 13/20] feature/suppress-in-individual-files ### What's done: * Fixed bug --- .../cqfn/diktat/ruleset/constants/Warnings.kt | 8 +++--- .../cqfn/diktat/ruleset/rules/FileNaming.kt | 6 ++-- .../cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 28 ++++++++----------- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt index 51b64dcd7b..86ae9d88c8 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt @@ -105,10 +105,10 @@ enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: S isFixMode: Boolean, freeText: String, offset: Int, - node: ASTNode?, + node: ASTNode, canBeAutoCorrected: Boolean = this.canBeAutoCorrected, autoFix: () -> Unit) { - if (node != null && node.hasSuppress(name)) + if (node.hasSuppress(name)) return warn(configRules, emit, canBeAutoCorrected, freeText, offset, node) fix(configRules, autoFix, isFixMode) @@ -120,8 +120,8 @@ enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: S autoCorrected: Boolean, freeText: String, offset: Int, - node: ASTNode?) { - if (node != null && node.hasSuppress(name)) + node: ASTNode) { + if (node.hasSuppress(name)) return if (configs.isRuleEnabled(this)) { diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/FileNaming.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/FileNaming.kt index c34be8fab7..3a6b35d5e6 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/FileNaming.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/FileNaming.kt @@ -42,16 +42,16 @@ class FileNaming(private val configRules: List) : Rule("file-naming if (node.elementType == FILE) { fileName = node.getUserData(KtLint.FILE_PATH_USER_DATA_KEY)!! - checkFileNaming() + checkFileNaming(node) checkClassNameMatchesWithFile(node) } } - private fun checkFileNaming() { + private fun checkFileNaming(node: ASTNode) { if (fileName != null) { val (name, extension) = getFileParts() if (!name.isPascalCase() || !VALID_EXTENSIONS.contains(extension)) { - FILE_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, "$name$extension", 0, null) { + FILE_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, "$name$extension", 0, node) { // FixMe: we can add an autocorrect here in future, but is there any purpose to change file or class name? } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index 32c6b4e34f..b8e0bf1c70 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -305,26 +305,22 @@ fun ASTNode?.isAccessibleOutside(): Boolean = } fun ASTNode.hasSuppress(warningName: String): Boolean { - return if (findChildByType(MODIFIER_LIST) != null) { - findChildByType(MODIFIER_LIST)?.findAllNodesWithSpecificType(ANNOTATION_ENTRY)?.any { - (it.psi as KtAnnotationEntry).shortName.toString() == "Suppress" - && (it.psi).text.contains(warningName) - } ?: false - } else { - parent({ node -> - node.findChildByType(MODIFIER_LIST) - ?.findAllNodesWithSpecificType(ANNOTATION_ENTRY) - ?.any { - (it.psi as KtAnnotationEntry).shortName.toString() == "Suppress" - && (it.psi).text.contains(warningName) - } ?: false - }) != null - } + return parent({ node -> + node.findChildByType(MODIFIER_LIST) + ?.findAllNodesWithSpecificType(ANNOTATION_ENTRY) + ?.any { + (it.psi as KtAnnotationEntry).shortName.toString() == Suppress::class.simpleName + && (it.psi as KtAnnotationEntry).valueArgumentList?.arguments?.isNotEmpty() ?: true + && (it.psi as KtAnnotationEntry).valueArgumentList?.arguments + ?.get(0)?.text?.contains(warningName) ?: true + } ?: false + }, strict = false) != null } + /** * creation of operation reference in a node */ -fun ASTNode.createOperationReference(elementType: IElementType, text: String){ +fun ASTNode.createOperationReference(elementType: IElementType, text: String) { val operationReference = CompositeElement(OPERATION_REFERENCE) this.addChild(operationReference, null) operationReference.addChild(LeafPsiElement(elementType, text), null) From d388db04cd42d1d35cb32d511706db1aefdc573f Mon Sep 17 00:00:00 2001 From: soWhoAmI Date: Mon, 14 Sep 2020 13:15:22 +0300 Subject: [PATCH 14/20] feature/suppress-in-individual-files ### What's done: * Added to README.md --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 84e6be51ee..4cc533c6eb 100644 --- a/README.md +++ b/README.md @@ -196,6 +196,19 @@ Note, that you can specify and put `diktat-analysis.yml` that contains configura See default configuration in [diktat-analysis.yml](diktat-rules/src/main/resources/diktat-analysis.yml) \ Also see [the list of all rules supported by diKTat](info/available-rules.md). +## Suppress warnings +You can suppress warnings by adding Suppress annotation + +For example: + +``` kotlin +@Suppress("FUNCTION_NAME_INCORRECT_CASE") +class SomeClass { + fun methODTREE(): String { + + } +} +``` ## How to contribute? Main components are: From cd1cd81745d7ec8a3a095c08fe4b105f604822a0 Mon Sep 17 00:00:00 2001 From: soWhoAmI Date: Mon, 14 Sep 2020 13:36:34 +0300 Subject: [PATCH 15/20] feature/suppress-in-individual-files ### What's done: * Fixed description --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4cc533c6eb..ded914fe14 100644 --- a/README.md +++ b/README.md @@ -196,8 +196,8 @@ Note, that you can specify and put `diktat-analysis.yml` that contains configura See default configuration in [diktat-analysis.yml](diktat-rules/src/main/resources/diktat-analysis.yml) \ Also see [the list of all rules supported by diKTat](info/available-rules.md). -## Suppress warnings -You can suppress warnings by adding Suppress annotation +## Suppress warnings on individual code blocks +In addition to enabling/disabling warning globally via config file, you can suppress warnings by adding `@Suppress` annotation on individual code blocks For example: From 7b8ebe3384b090170184d9d5c02aeb1d10d8bfa5 Mon Sep 17 00:00:00 2001 From: soWhoAmI Date: Mon, 14 Sep 2020 14:50:12 +0300 Subject: [PATCH 16/20] feature/suppress-in-individual-files ### What's done: * Fixed bugs --- .../org/cqfn/diktat/ruleset/constants/Warnings.kt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt index 86ae9d88c8..e1541a796e 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt @@ -108,10 +108,8 @@ enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: S node: ASTNode, canBeAutoCorrected: Boolean = this.canBeAutoCorrected, autoFix: () -> Unit) { - if (node.hasSuppress(name)) - return warn(configRules, emit, canBeAutoCorrected, freeText, offset, node) - fix(configRules, autoFix, isFixMode) + fix(configRules, autoFix, isFixMode, node) } @Suppress("LongParameterList") @@ -121,10 +119,8 @@ enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: S freeText: String, offset: Int, node: ASTNode) { - if (node.hasSuppress(name)) - return - if (configs.isRuleEnabled(this)) { + if (configs.isRuleEnabled(this) && !node.hasSuppress(name)) { emit(offset, "${this.warnText()} $freeText", autoCorrected @@ -132,8 +128,8 @@ enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: S } } - private inline fun fix(configs: List, autoFix: () -> Unit, isFix: Boolean) { - if (configs.isRuleEnabled(this) && isFix) { + private inline fun fix(configs: List, autoFix: () -> Unit, isFix: Boolean, node: ASTNode) { + if (configs.isRuleEnabled(this) && isFix && !node.hasSuppress(name)) { autoFix() } } From 4e07ce2ef74f4251212ea0fc9378c8352d649759 Mon Sep 17 00:00:00 2001 From: aktsay Date: Tue, 15 Sep 2020 14:57:29 +0300 Subject: [PATCH 17/20] feature/suppress-in-individual-files ### What's done: * Fixed bugs --- .../rules/identifiers/LocalVariablesRule.kt | 2 +- .../cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 18 ++++-- .../cqfn/diktat/ruleset/utils/SuppressTest.kt | 60 +++++++++++++++++++ 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/identifiers/LocalVariablesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/identifiers/LocalVariablesRule.kt index 8d29759f21..122545e7e3 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/identifiers/LocalVariablesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/identifiers/LocalVariablesRule.kt @@ -121,7 +121,7 @@ class LocalVariablesRule(private val configRules: List) : Rule("loc if (firstUsageStatementLine - numLinesToSkip != property.node.lastLineNumber()!! + 1 + offset) { LOCAL_VARIABLE_EARLY_DECLARATION.warn(configRules, emitWarn, isFixMode, warnMessage(property.name!!, property.node.lineNumber()!!, firstUsageLine - ?: firstUsageStatementLine), property.startOffset) + ?: firstUsageStatementLine), property.startOffset, property.node) } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index 508fe9a611..8e67ef41a6 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -5,6 +5,7 @@ import com.pinterest.ktlint.core.ast.ElementType.ANNOTATION import com.pinterest.ktlint.core.ast.ElementType.ANNOTATION_ENTRY import com.pinterest.ktlint.core.ast.ElementType.CONST_KEYWORD import com.pinterest.ktlint.core.ast.ElementType.FILE +import com.pinterest.ktlint.core.ast.ElementType.FILE_ANNOTATION_LIST import com.pinterest.ktlint.core.ast.ElementType.INTERNAL_KEYWORD import com.pinterest.ktlint.core.ast.ElementType.LBRACE import com.pinterest.ktlint.core.ast.ElementType.MODIFIER_LIST @@ -308,13 +309,18 @@ fun ASTNode?.isAccessibleOutside(): Boolean = fun ASTNode.hasSuppress(warningName: String): Boolean { return parent({ node -> - node.findChildByType(MODIFIER_LIST) - ?.findAllNodesWithSpecificType(ANNOTATION_ENTRY) + val children: ASTNode? = if (node.elementType != FILE) { + node.findChildByType(MODIFIER_LIST) + } else { + node.findChildByType(FILE_ANNOTATION_LIST) + } + children?.findAllNodesWithSpecificType(ANNOTATION_ENTRY) + ?.map { it.psi as KtAnnotationEntry } ?.any { - (it.psi as KtAnnotationEntry).shortName.toString() == Suppress::class.simpleName - && (it.psi as KtAnnotationEntry).valueArgumentList?.arguments?.isNotEmpty() ?: true - && (it.psi as KtAnnotationEntry).valueArgumentList?.arguments - ?.get(0)?.text?.contains(warningName) ?: true + it.shortName.toString() == Suppress::class.simpleName + && it.valueArgumentList?.arguments?.isNotEmpty() ?: false // don't suppress anything + && it.valueArgumentList?.arguments + ?.firstOrNull()?.text?.contains(warningName) ?: true } ?: false }, strict = false) != null } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt index c044e80e5b..0af1fad246 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt @@ -66,4 +66,64 @@ class SuppressTest : LintTestBase(::IdentifierNaming) { ) } + @Test + fun `test suppress on file`() { + val code = + """ + @file:Suppress("FUNCTION_NAME_INCORRECT_CASE") + + class SomeClass { + fun /* */ methODTREE(): String { + + } + } + """.trimIndent() + lintMethod(code) + } + + @Test + fun `test suppress field`() { + val code = + """ + class SomeClass(@field:Suppress("IDENTIFIER_LENGTH") val a:String) { + fun /* */ method(): String { + + } + } + """.trimIndent() + lintMethod(code) + } + + @Test + fun `test suppress field with set`() { + val code = + """ + class SomeClass() { + @set:[Suppress("IDENTIFIER_LENGTH") Inject] + val a = 5 + + fun /* */ method(): String { + + } + } + """.trimIndent() + lintMethod(code) + } + + @Test + fun `check simple wrong enum`() { + lintMethod( + """ + |@set:[Suppress("WRONG_DECLARATION_ORDER") Suppress("IDENTIFIER_LENGTH") Suppress("CONFUSING_IDENTIFIER_NAMING")] + |enum class Alph { + | D, + | C, + | A, + | B, + | ; + |} + """.trimMargin() + ) + } + } From 826b47f10f8f365b73ed2bdf1e14d01b43276513 Mon Sep 17 00:00:00 2001 From: aktsay Date: Tue, 15 Sep 2020 15:09:28 +0300 Subject: [PATCH 18/20] feature/suppress-in-individual-files ### What's done: * Added test --- .../cqfn/diktat/ruleset/utils/SuppressTest.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt index 0af1fad246..726c205129 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt @@ -126,4 +126,20 @@ class SuppressTest : LintTestBase(::IdentifierNaming) { ) } + @Test + fun `test suppress on class bad`() { + val code = + """ + @Suppress() + class SomeClass { + fun /* */ methODTREE(): String { + + } + } + """.trimIndent() + lintMethod(code, + LintError(3,13, "$DIKTAT_RULE_SET_ID:identifier-naming", + "${Warnings.FUNCTION_NAME_INCORRECT_CASE.warnText()} methODTREE", true)) + } + } From 6a1c1194e8d4576e4962b56fd424684e8ec57a2b Mon Sep 17 00:00:00 2001 From: aktsay Date: Tue, 15 Sep 2020 15:38:02 +0300 Subject: [PATCH 19/20] feature/suppress-in-individual-files ### What's done: * Fixed bugs --- .../main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index 8e67ef41a6..94ed4b9c68 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -318,9 +318,8 @@ fun ASTNode.hasSuppress(warningName: String): Boolean { ?.map { it.psi as KtAnnotationEntry } ?.any { it.shortName.toString() == Suppress::class.simpleName - && it.valueArgumentList?.arguments?.isNotEmpty() ?: false // don't suppress anything && it.valueArgumentList?.arguments - ?.firstOrNull()?.text?.contains(warningName) ?: true + ?.firstOrNull()?.text?.contains(warningName) ?: false } ?: false }, strict = false) != null } From 0a4073449da31298e9755a0d8393d498a924ae98 Mon Sep 17 00:00:00 2001 From: aktsay Date: Tue, 15 Sep 2020 16:30:16 +0300 Subject: [PATCH 20/20] feature/suppress-in-individual-files ### What's done: * Fixed bugs --- .../kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 6 +++--- .../cqfn/diktat/ruleset/chapter3/LocalVariablesWarnTest.kt | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt index 94ed4b9c68..0a79185302 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt @@ -309,17 +309,17 @@ fun ASTNode?.isAccessibleOutside(): Boolean = fun ASTNode.hasSuppress(warningName: String): Boolean { return parent({ node -> - val children: ASTNode? = if (node.elementType != FILE) { + val annotationNode = if (node.elementType != FILE) { node.findChildByType(MODIFIER_LIST) } else { node.findChildByType(FILE_ANNOTATION_LIST) } - children?.findAllNodesWithSpecificType(ANNOTATION_ENTRY) + annotationNode?.findAllNodesWithSpecificType(ANNOTATION_ENTRY) ?.map { it.psi as KtAnnotationEntry } ?.any { it.shortName.toString() == Suppress::class.simpleName && it.valueArgumentList?.arguments - ?.firstOrNull()?.text?.contains(warningName) ?: false + ?.firstOrNull()?.text?.trim('"', ' ').equals(warningName) ?: false } ?: false }, strict = false) != null } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/LocalVariablesWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/LocalVariablesWarnTest.kt index 44e2477c4d..9de6f71e15 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/LocalVariablesWarnTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/LocalVariablesWarnTest.kt @@ -34,6 +34,7 @@ class LocalVariablesWarnTest : LintTestBase(::LocalVariablesRule) { fun `local variables used only in this scope - positive example`() { lintMethod( """ + |import org.diktat.Some as test |class Example { | fun foo() { | val bar = 0