diff --git a/README.md b/README.md index 84e6be51ee..ded914fe14 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 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: + +``` kotlin +@Suppress("FUNCTION_NAME_INCORRECT_CASE") +class SomeClass { + fun methODTREE(): String { + + } +} +``` ## How to contribute? Main components are: 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 fb2490da07..95c7b90de6 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 /** * This class represent individual inspections of diktat code style. @@ -104,18 +106,22 @@ enum class Warnings(private val canBeAutoCorrected: Boolean, private val warn: S isFixMode: Boolean, freeText: String, offset: Int, + node: ASTNode, canBeAutoCorrected: Boolean = this.canBeAutoCorrected, autoFix: () -> Unit) { - warn(configRules, emit, canBeAutoCorrected, freeText, offset) - fix(configRules, autoFix, isFixMode) + warn(configRules, emit, canBeAutoCorrected, freeText, offset, node) + fix(configRules, autoFix, isFixMode, node) } + @Suppress("LongParameterList") fun warn(configs: List, emit: ((offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit), autoCorrected: Boolean, freeText: String, - offset: Int) { - if (configs.isRuleEnabled(this)) { + offset: Int, + node: ASTNode) { + + if (configs.isRuleEnabled(this) && !node.hasSuppress(name)) { emit(offset, "${this.warnText()} $freeText", autoCorrected @@ -123,8 +129,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() } } 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/BlockStructureBraces.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/BlockStructureBraces.kt index 80c0b69e22..1138db2095 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 @@ -157,7 +157,7 @@ class BlockStructureBraces(private val configRules: List) : Rule("b 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 { @@ -173,7 +173,7 @@ class BlockStructureBraces(private val configRules: List) : Rule("b 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, newNode) { if (newNode.elementType != WHITE_SPACE) { node.addChild(PsiWhiteSpaceImpl("\n"), newNode) } else { @@ -187,7 +187,7 @@ class BlockStructureBraces(private val configRules: List) : Rule("b allMiddleSpace.forEach { if (checkBraceNode(it, true)) { BRACES_BLOCK_STRUCTURE_ERROR.warnAndFix(configRules, emitWarn, isFixMode, "incorrect new line after closing brace", - it.startOffset) { + it.startOffset, it) { if (it.elementType != WHITE_SPACE) { node.addChild(PsiWhiteSpaceImpl(" "), node.findChildByType(keyword)) } else { @@ -203,7 +203,7 @@ class BlockStructureBraces(private val configRules: List) : Rule("b 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 6a29782c18..2e7f76957e 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 @@ -69,7 +69,7 @@ class BracesInConditionalsAndLoopsRule(private val configRules: List) : 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 { @@ -100,7 +100,7 @@ class ClassLikeStructuresOrderRule(private val configRules: List) : 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 01116daf68..6c3d52b8e2 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 @@ -65,7 +65,7 @@ class ConsecutiveSpacesRule(private val configRules: List) : Rule(" 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 73747c9471..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 @@ -41,21 +41,22 @@ class EmptyBlock(private val configRules: List) : Rule("empty-block if (node.isBlockEmpty()) { if (!configuration.emptyBlockExist) { EMPTY_BLOCK_STRUCTURE_ERROR.warn(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 { + } + 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", - 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 653a4cdcbc..4a9c451edb 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 @@ -51,7 +51,7 @@ class EnumsSeparated(private val configRules: List) : Rule("enum-se enumEntries.forEach { if (!it.treeNext.isWhiteSpaceWithNewline()) ENUMS_SEPARATED.warnAndFix(configRules, emitWarn, isFixMode, "enum entries must end with a line break", - it.startOffset) { + it.startOffset, it) { it.appendNewlineMergingWhiteSpace(it.treeNext, it.treeNext) } } @@ -72,19 +72,19 @@ class EnumsSeparated(private val configRules: List) : Rule("enum-se 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 939c7f3623..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) { + 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? } } @@ -65,7 +65,7 @@ class FileNaming(private val configRules: List) : 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 922050de0b..da21c35e83 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 @@ -122,7 +122,7 @@ class IdentifierNaming(private val configRules: List) : Rule("ident 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, identifier) } return true } @@ -141,7 +141,7 @@ class IdentifierNaming(private val configRules: List) : Rule("ident // 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 if identifier of a property has a confusing name if (CONFUSING_IDENTIFIER_NAMES.contains(variableName.text) && !validCatchIdentifier(variableName) @@ -152,13 +152,13 @@ class IdentifierNaming(private val configRules: List) : Rule("ident // 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 != "_" && !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()) } @@ -172,7 +172,7 @@ class IdentifierNaming(private val configRules: List) : Rule("ident .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()) } } @@ -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) } /** @@ -228,14 +228,14 @@ class IdentifierNaming(private val configRules: List) : Rule("ident 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, genericType) { // 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, className) { (className as LeafPsiElement).replaceWithText(className.text.toPascalCase()) } } @@ -260,7 +260,7 @@ class IdentifierNaming(private val configRules: List) : Rule("ident ?.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, classNameNode) { // FixMe: need to add tests for this (classNameNode as LeafPsiElement).replaceWithText(classNameNode.text + "Exception") } @@ -275,7 +275,7 @@ class IdentifierNaming(private val configRules: List) : Rule("ident // 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, objectName) { (objectName as LeafPsiElement).replaceWithText(objectName.text.toPascalCase()) } } @@ -291,7 +291,7 @@ class IdentifierNaming(private val configRules: List) : Rule("ident 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, value) { // FixMe: add tests for this (value as LeafPsiElement).replaceWithText(value.text.toUpperSnakeCase()) } @@ -317,7 +317,7 @@ class IdentifierNaming(private val configRules: List) : Rule("ident // 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, functionName) { // FixMe: add tests for this (functionName as LeafPsiElement).replaceWithText(functionName.text.toLowerCamelCase()) } @@ -329,7 +329,7 @@ class IdentifierNaming(private val configRules: List) : Rule("ident // 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, functionName) { // FixMe: add agressive autofix for this } } @@ -362,7 +362,7 @@ class IdentifierNaming(private val configRules: List) : Rule("ident 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 15f74da672..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) { + offset + node.startOffset, node, fixableType != LongLineFixableCases.None) { fixError(fixableType) } } 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 1852dae9bf..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 @@ -30,8 +30,8 @@ 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 d234f0a746..c39d3c6e74 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 @@ -47,7 +47,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 nodeAfter = modifierNode.treeNext node.removeChild(modifierNode) node.addChild((sortModifierListOfPair[index].clone() as ASTNode), nodeAfter) @@ -61,7 +61,7 @@ class MultipleModifiersSequence(private val configRules: List) : Ru node.getChildren(null).filterIndexed { index, astNode -> astNode.elementType == ANNOTATION_ENTRY && index > firstModifierIndex }.forEach { WRONG_MULTIPLE_MODIFIERS_ORDER.warnAndFix(configRules, emitWarn, isFixMode, "${it.text} annotation should be before all modifiers", - it.startOffset) { + it.startOffset, it) { val spaceBefore = it.treePrev node.removeChild(it) if (spaceBefore != null && spaceBefore.elementType == WHITE_SPACE){ 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 61b0ca1691..ad97367d46 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 @@ -93,7 +93,7 @@ class PackageNaming(private val configRules: List) : Rule("package- * 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) @@ -138,14 +138,14 @@ class PackageNaming(private val configRules: List) : Rule("package- 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)) } @@ -154,7 +154,7 @@ class PackageNaming(private val configRules: List) : Rule("package- // 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) } @@ -175,7 +175,7 @@ class PackageNaming(private val configRules: List) : Rule("package- */ 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("_", "")) } } @@ -242,7 +242,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) { + 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 4b9ab1e426..d977c6c811 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 @@ -36,7 +36,7 @@ class SingleLineStatementsRule(private val configRules: List) : Rul 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/SortRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SortRule.kt index 2b2a121ec0..e2f2ccb44d 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, mutableList.first()) { 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/StringConcatenationRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/StringConcatenationRule.kt index 9e280e82d2..1b3af0fe9e 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 @@ -48,7 +48,7 @@ class StringConcatenationRule(private val configRules: List) : Rule 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/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/WhiteSpaceRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/WhiteSpaceRule.kt index 7f97948231..3832e20c2f 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 @@ -130,7 +130,7 @@ class WhiteSpaceRule(private val configRules: List) : Rule("horizon 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) } } @@ -145,7 +145,7 @@ class WhiteSpaceRule(private val configRules: List) : Rule("horizon 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, nextCodeLeaf) { node.leaveSingleWhiteSpace() } } @@ -173,13 +173,13 @@ class WhiteSpaceRule(private val configRules: List) : Rule("horizon 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(private val configRules: List) : Rule("horizon 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(private val configRules: List) : Rule("horizon // 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 cb631961b8..d9cb3a38b7 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 @@ -83,7 +83,7 @@ class CommentsRule(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/comments/HeaderCommentRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/HeaderCommentRule.kt index fdb7c86f8b..584a98b953 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 @@ -73,7 +73,7 @@ class HeaderCommentRule(private val configRules: List) : Rule("head } 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) } @@ -103,7 +103,7 @@ class HeaderCommentRule(private val configRules: List) : Rule("head 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 { @@ -124,7 +124,7 @@ class HeaderCommentRule(private val configRules: List) : Rule("head 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 @@ -132,13 +132,13 @@ class HeaderCommentRule(private val configRules: List) : Rule("head .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 146305b418..4c8d7eea63 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 @@ -45,7 +45,7 @@ class BlankLinesRule(private val configRules: List) : Rule("blank-l // 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() } } @@ -53,7 +53,7 @@ class BlankLinesRule(private val configRules: List) : Rule("blank-l } 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 527edda65a..927e34e7d4 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 @@ -64,7 +64,7 @@ class FileSize(private val configRules: List) : Rule("file-size") { .split("\n") .size if (size > maxSize) { - FILE_IS_TOO_LONG.warn(this.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 744d30a259..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 @@ -65,7 +65,7 @@ class FileStructureRule(private val configRules: List) : Rule("file 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(private val configRules: List) : Rule("file 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(private val configRules: List) : Rule("file 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", "")}") } } @@ -106,13 +106,14 @@ class FileStructureRule(private val configRules: List) : Rule("file val imports = node.getChildren(TokenSet.create(IMPORT_DIRECTIVE)).toList() // 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 && it.text !in configuration.allowedWildcards }.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 61c4c1aca7..69ae848e51 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 @@ -99,7 +99,7 @@ class IndentationRule(private val configRules: List) : Rule("indent .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))) } } @@ -113,7 +113,7 @@ class IndentationRule(private val configRules: List) : Rule("indent 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 { @@ -167,7 +167,7 @@ class IndentationRule(private val configRules: List) : Rule("indent } 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, whiteSpace.node) { 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 de0d819e0c..28f66f6ffe 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 @@ -114,7 +114,7 @@ class NewlinesRule(private val configRules: List) : 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) } } @@ -130,7 +130,7 @@ class NewlinesRule(private val configRules: List) : 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()) { @@ -161,7 +161,7 @@ class NewlinesRule(private val configRules: List) : 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 @@ -188,7 +188,7 @@ class NewlinesRule(private val configRules: List) : 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) } } @@ -203,7 +203,7 @@ class NewlinesRule(private val configRules: List) : 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) } } @@ -221,7 +221,7 @@ class NewlinesRule(private val configRules: List) : 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, arrowNode) { // fixme: replacement logic can be sophisticated for better appearance? newlinesBeforeArrow.forEach { it.treeParent.replaceChild(it, PsiWhiteSpaceImpl(" ")) } arrowNode.treeNext.takeIf { it.elementType == WHITE_SPACE }?.leaveOnlyOneNewLine() @@ -232,7 +232,7 @@ class NewlinesRule(private val configRules: List) : 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, lbraceNode) { lbraceNode.treeNext.let { if (it.elementType == WHITE_SPACE) { it.leaveOnlyOneNewLine() @@ -260,7 +260,7 @@ class NewlinesRule(private val configRules: List) : 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/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/rules/kdoc/KdocComments.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocComments.kt index 07cabdfd60..d78abed59c 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 @@ -76,7 +76,7 @@ class KdocComments(private val configRules: List) : Rule("kdoc-comm 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 09bb909216..745b4db571 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 @@ -84,7 +84,7 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo 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() } } @@ -97,9 +97,9 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo } ?: false if (!isKdocNotEmpty) { KDOC_EMPTY_KDOC.warn(configRules, emitWarn, isFixMode, - node.treeParent.getIdentifierName()?.text - ?: node.nextSibling { it.elementType in KtTokens.KEYWORDS }?.text - ?: fileName, node.startOffset) + node.treeParent.getIdentifierName()?.text + ?: node.nextSibling { it.elementType in KtTokens.KEYWORDS }?.text + ?: fileName, node.startOffset, node) } return isKdocNotEmpty } @@ -107,19 +107,18 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo private fun checkNoDeprecatedTag(node: ASTNode) { val kDocTags = node.kDocTags() kDocTags?.find { it.name == "deprecated" } - ?.let { kDocTag -> - KDOC_NO_DEPRECATED_TAG.warnAndFix(configRules, emitWarn, isFixMode, kDocTag.text, kDocTag.node.startOffset) { - val kDocSection = kDocTag.node.treeParent - val deprecatedTagNode = kDocSection.getChildren(TokenSet.create(KDOC_TAG)) - .find { "@deprecated" in it.text }!! - kDocSection.removeRange(deprecatedTagNode.prevSibling { it.elementType == WHITE_SPACE }!!, - deprecatedTagNode.nextSibling { it.elementType == WHITE_SPACE } - ) - node.treeParent.addChild(LeafPsiElement(ElementType.ANNOTATION, - "@Deprecated(message = \"${kDocTag.getContent()}\")"), node.treeNext) - // copy to get all necessary indents - node.treeParent.addChild(node.nextSibling { it.elementType == WHITE_SPACE }!!.clone() as PsiWhiteSpaceImpl, node.treeNext) - } + ?.let { kDocTag -> + 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 }!! + kDocSection.removeRange(deprecatedTagNode.prevSibling { it.elementType == WHITE_SPACE }!!, + deprecatedTagNode.nextSibling { it.elementType == WHITE_SPACE } + ) + node.treeParent.addChild(LeafPsiElement(ElementType.ANNOTATION, + "@Deprecated(message = \"${kDocTag.getContent()}\")"), node.treeNext) + // copy to get all necessary indents + node.treeParent.addChild(node.nextSibling { it.elementType == WHITE_SPACE }!!.clone() as PsiWhiteSpaceImpl, node.treeNext) } } } @@ -127,7 +126,7 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo 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) } } @@ -143,7 +142,7 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo hasSubject && isSpaceBeforeContentError || isSpaceAfterTagError }?.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 +181,7 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo 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, basicTags.first().node) { val kDocSection = node.getFirstChildWithType(KDOC_SECTION)!! val basicTagChildren = kDocTags .filter { basicTagsOrdered.contains(it.knownTag) } @@ -212,7 +211,7 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo 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 +247,7 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo 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 @@ -275,8 +274,8 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo 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.joinToString(", ") { "@${(it.psi as KDocTag).name!!}" }, + 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 4fc9b868e1..7b800bdf37 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 @@ -100,7 +100,7 @@ class KdocMethods(private val configRules: List) : Rule("kdoc-metho 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) } } @@ -143,13 +143,15 @@ class KdocMethods(private val configRules: List) : Rule("kdoc-metho missingParameters: Collection, kDocMissingParameters: List, kDocTags: Collection?) { + kDocMissingParameters.forEach { KDOC_WITHOUT_PARAM_TAG.warn(configRules, emitWarn, false, - "${it.getSubjectName()} param isn't present in argument list", it.node.startOffset) + "${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, - "${node.getIdentifierName()!!.text} (${missingParameters.joinToString()})", node.startOffset) { + "${node.getIdentifierName()!!.text} (${missingParameters.joinToString()})", node.startOffset, node) { val beforeTag = kDocTags?.find { it.knownTag == KDocKnownTag.RETURN } ?: kDocTags?.find { it.knownTag == KDocKnownTag.THROWS } missingParameters.forEach { @@ -167,7 +169,8 @@ class KdocMethods(private val configRules: List) : Rule("kdoc-metho kDoc: ASTNode?, kDocTags: Collection? ) { - KDOC_WITHOUT_RETURN_TAG.warnAndFix(configRules, emitWarn, isFixMode, node.getIdentifierName()!!.text, node.startOffset) { + KDOC_WITHOUT_RETURN_TAG.warnAndFix(configRules, emitWarn, isFixMode, node.getIdentifierName()!!.text, + node.startOffset, node) { val beforeTag = kDocTags?.find { it.knownTag == KDocKnownTag.THROWS } kDoc?.insertTagBefore(beforeTag?.node) { addChild(LeafPsiElement(KDOC_TAG_NAME, "@return")) @@ -180,7 +183,7 @@ class KdocMethods(private val configRules: List) : Rule("kdoc-metho missingExceptions: Collection ) { KDOC_WITHOUT_THROWS_TAG.warnAndFix(configRules, emitWarn, isFixMode, - "${node.getIdentifierName()!!.text} (${missingExceptions.joinToString()})", node.startOffset) { + "${node.getIdentifierName()!!.text} (${missingExceptions.joinToString()})", node.startOffset, node) { missingExceptions.forEach { kDoc?.insertTagBefore(null) { addChild(LeafPsiElement(KDOC_TAG_NAME, "@throws")) @@ -197,7 +200,7 @@ class KdocMethods(private val configRules: List) : Rule("kdoc-metho 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 "") + @@ -215,7 +218,7 @@ class KdocMethods(private val configRules: List) : Rule("kdoc-metho 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 bf485c7a20..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 @@ -1,8 +1,11 @@ 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.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 @@ -23,6 +26,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 @@ -303,10 +307,27 @@ fun ASTNode?.isAccessibleOutside(): Boolean = true } +fun ASTNode.hasSuppress(warningName: String): Boolean { + return parent({ node -> + val annotationNode = if (node.elementType != FILE) { + node.findChildByType(MODIFIER_LIST) + } else { + node.findChildByType(FILE_ANNOTATION_LIST) + } + annotationNode?.findAllNodesWithSpecificType(ANNOTATION_ENTRY) + ?.map { it.psi as KtAnnotationEntry } + ?.any { + it.shortName.toString() == Suppress::class.simpleName + && it.valueArgumentList?.arguments + ?.firstOrNull()?.text?.trim('"', ' ').equals(warningName) ?: false + } ?: 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) 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 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..726c205129 --- /dev/null +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/SuppressTest.kt @@ -0,0 +1,145 @@ +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 on class`() { + val code = + """ + @Suppress("FUNCTION_NAME_INCORRECT_CASE") + class SomeClass { + fun /* */ methODTREE(): String { + + } + } + """.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() + ) + } + + @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() + ) + } + + @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)) + } + +}