From 166d61535469dab9956306131d536acfcfefe003 Mon Sep 17 00:00:00 2001
From: Alexander Tsay <48321920+aktsay6@users.noreply.github.com>
Date: Wed, 13 Jan 2021 07:50:51 +0300
Subject: [PATCH 01/11] bugfix/bug-documentation-generation (#701)
### What's done:
* Fixed bug
---
info/buildSrc/gradle.properties | 2 +-
.../cqfn/diktat/generation/docs/LatexUtils.kt | 2 +-
wp/sections/appendix.tex | 236 +++++++++---------
3 files changed, 120 insertions(+), 120 deletions(-)
diff --git a/info/buildSrc/gradle.properties b/info/buildSrc/gradle.properties
index 66cd2f5a2f..f9343f1708 100644
--- a/info/buildSrc/gradle.properties
+++ b/info/buildSrc/gradle.properties
@@ -1 +1 @@
-version=0.2.1-SNAPSHOT
\ No newline at end of file
+version=0.3.1-SNAPSHOT
\ No newline at end of file
diff --git a/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/LatexUtils.kt b/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/LatexUtils.kt
index f31f2fcb8f..dde58772f0 100644
--- a/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/LatexUtils.kt
+++ b/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/LatexUtils.kt
@@ -9,7 +9,7 @@ import java.io.PrintWriter
@Suppress("VARIABLE_NAME_INCORRECT_FORMAT")
val NUMBER_IN_TAG = Regex("\"([a-z0-9.]*)\"") // finds "r1.0.2"
@Suppress("VARIABLE_NAME_INCORRECT_FORMAT")
-val RULE_NAME = Regex("([A-Za-z 0-9'.-]*)") // get's rule name from ### ... Rule name
+val RULE_NAME = Regex("([A-Za-z 0-9':.-]*)") // get's rule name from ### ... Rule name
@Suppress("VARIABLE_NAME_INCORRECT_FORMAT")
val BOLD_TEXT = Regex("""\*\*([^*]+)\*\*""") // finds bold text in regular lines
@Suppress("VARIABLE_NAME_INCORRECT_FORMAT")
diff --git a/wp/sections/appendix.tex b/wp/sections/appendix.tex
index 6ad32803fc..ef8f2b9eae 100644
--- a/wp/sections/appendix.tex
+++ b/wp/sections/appendix.tex
@@ -16,113 +16,113 @@ \section*{Available Rules}
\hline
\textbf{diKTat rule} & \textbf{code style} & \textbf{autofix} & \textbf{config} \\
\hline
-VARIABLE\underline{ }NAME\underline{ }INCORRECT & \hyperref[sec:1.1.1]{ 1.1.1} & no & no \\
-VARIABLE\underline{ }HAS\underline{ }PREFIX & \hyperref[sec:1.1.1]{ 1.1.1} & yes & no \\
-IDENTIFIER\underline{ }LENGTH & \hyperref[sec:1.1.1]{ 1.1.1} & no & no \\
-GENERIC\underline{ }NAME & \hyperref[sec:1.1.1]{ 1.1.1} & yes & no \\
-BACKTICKS\underline{ }PROHIBITED & \hyperref[sec:1.1.1]{ 1.1.1} & no & no \\
-FILE\underline{ }NAME\underline{ }INCORRECT & \hyperref[sec:1.1.1]{ 1.1.1} & yes & no \\
-EXCEPTION\underline{ }SUFFIX & \hyperref[sec:1.1.1]{ 1.1.1} & yes & no \\
-CONFUSING\underline{ }IDENTIFIER\underline{ }NAMING & \hyperref[sec:1.1.1]{ 1.1.1} & no & no \\
-PACKAGE\underline{ }NAME\underline{ }MISSING & \hyperref[sec:1.2.1]{ 1.2.1} & yes & no \\
-PACKAGE\underline{ }NAME\underline{ }INCORRECT\underline{ }CASE & \hyperref[sec:1.2.1]{ 1.2.1} & yes & no \\
-PACKAGE\underline{ }NAME\underline{ }INCORRECT\underline{ }PREFIX & \hyperref[sec:1.2.1]{ 1.2.1} & yes & no \\
-PACKAGE\underline{ }NAME\underline{ }INCORRECT\underline{ }SYMBOLS & \hyperref[sec:1.2.1]{ 1.2.1} & no & no \\
-PACKAGE\underline{ }NAME\underline{ }INCORRECT\underline{ }PATH & \hyperref[sec:1.2.1]{ 1.2.1} & yes & no \\
-INCORRECT\underline{ }PACKAGE\underline{ }SEPARATOR & \hyperref[sec:1.2.1]{ 1.2.1} & yes & no \\
-CLASS\underline{ }NAME\underline{ }INCORRECT & \hyperref[sec:1.3.1]{ 1.3.1} & yes & no \\
-OBJECT\underline{ }NAME\underline{ }INCORRECT & \hyperref[sec:1.3.1]{ 1.3.1} & yes & no \\
-ENUM\underline{ }VALUE & \hyperref[sec:1.3.1]{ 1.3.1} & yes & enumStyle: snakeCase, pascalCase \\
-FUNCTION\underline{ }NAME\underline{ }INCORRECT\underline{ }CASE & \hyperref[sec:1.4.1]{ 1.4.1} & yes & no \\
-CONSTANT\underline{ }UPPERCASE & \hyperref[sec:1.5.1]{ 1.5.1} & yes & no \\
-VARIABLE\underline{ }NAME\underline{ }INCORRECT\underline{ }FORMAT & \hyperref[sec:1.6.1]{ 1.6.1} & yes & no \\
-FUNCTION\underline{ }BOOLEAN\underline{ }PREFIX & \hyperref[sec:1.6.2]{ 1.6.2} & yes & no \\
-MISSING\underline{ }KDOC\underline{ }TOP\underline{ }LEVEL & \hyperref[sec:2.1.1]{ 2.1.1} & no & no \\
-MISSING\underline{ }KDOC\underline{ }CLASS\underline{ }ELEMENTS & \hyperref[sec:2.1.1]{ 2.1.1} & no & no \\
-MISSING\underline{ }KDOC\underline{ }ON\underline{ }FUNCTION & \hyperref[sec:2.1.1]{ 2.1.1} & yes & no \\
-KDOC\underline{ }NO\underline{ }CONSTRUCTOR\underline{ }PROPERTY & \hyperref[sec:2.1.1]{ 2.1.1} & yes & no \\
-KDOC\underline{ }EXTRA\underline{ }PROPERTY & \hyperref[sec:2.1.1]{ 2.1.1} & no & no \\
-KDOC\underline{ }NO\underline{ }CONSTRUCTOR\underline{ }PROPERTY\underline{ }WITH\underline{ }COMMENT & \hyperref[sec:2.1.1]{ 2.1.1} & yes & no \\
-KDOC\underline{ }WITHOUT\underline{ }PARAM\underline{ }TAG & \hyperref[sec:2.1.2]{ 2.1.2} & yes & no \\
-KDOC\underline{ }WITHOUT\underline{ }RETURN\underline{ }TAG & \hyperref[sec:2.1.2]{ 2.1.2} & yes & no \\
-KDOC\underline{ }WITHOUT\underline{ }THROWS\underline{ }TAG & \hyperref[sec:2.1.2]{ 2.1.2} & yes & no \\
-KDOC\underline{ }EMPTY\underline{ }KDOC & \hyperref[sec:2.1.3]{ 2.1.3} & no & no \\
-KDOC\underline{ }WRONG\underline{ }SPACES\underline{ }AFTER\underline{ }TAG & \hyperref[sec:2.1.3]{ 2.1.3} & yes & no \\
-KDOC\underline{ }WRONG\underline{ }TAGS\underline{ }ORDER & \hyperref[sec:2.1.3]{ 2.1.3} & yes & no \\
-KDOC\underline{ }NEWLINES\underline{ }BEFORE\underline{ }BASIC\underline{ }TAGS & \hyperref[sec:2.1.3]{ 2.1.3} & yes & no \\
-KDOC\underline{ }NO\underline{ }NEWLINES\underline{ }BETWEEN\underline{ }BASIC\underline{ }TAGS & \hyperref[sec:2.1.3]{ 2.1.3} & yes & no \\
-KDOC\underline{ }NO\underline{ }NEWLINE\underline{ }AFTER\underline{ }SPECIAL\underline{ }TAGS & \hyperref[sec:2.1.3]{ 2.1.3} & yes & no \\
-KDOC\underline{ }NO\underline{ }DEPRECATED\underline{ }TAG & \hyperref[sec:2.1.3]{ 2.1.3} & yes & no \\
-KDOC\underline{ }NO\underline{ }EMPTY\underline{ }TAGS & \hyperref[sec:2.2.1]{ 2.2.1} & no & no \\
-HEADER\underline{ }WRONG\underline{ }FORMAT & \hyperref[sec:2.2.1]{ 2.2.1} & yes & no \\
-HEADER\underline{ }MISSING\underline{ }OR\underline{ }WRONG\underline{ }COPYRIGHT & \hyperref[sec:2.2.1]{ 2.2.1} & yes & mandatoryCopyright \\
-WRONG\underline{ }COPYRIGHT\underline{ }YEAR & \hyperref[sec:2.2.1]{ 2.2.1} & yes & no \\
-HEADER\underline{ }CONTAINS\underline{ }DATE\underline{ }OR\underline{ }AUTHOR & \hyperref[sec:2.2.1]{ 2.2.1} & no & no \\
-HEADER\underline{ }MISSING\underline{ }IN\underline{ }NON\underline{ }SINGLE\underline{ }CLASS\underline{ }FILE & \hyperref[sec:2.2.1]{ 2.2.1} & no & no \\
-HEADER\underline{ }NOT\underline{ }BEFORE\underline{ }PACKAGE & \hyperref[sec:2.2.1]{ 2.2.1} & yes & no \\
-KDOC\underline{ }TRIVIAL\underline{ }KDOC\underline{ }ON\underline{ }FUNCTION & \hyperref[sec:2.3.1]{ 2.3.1} & no & no \\
-WRONG\underline{ }NEWLINES\underline{ }AROUND\underline{ }KDOC & \hyperref[sec:2.4.1]{ 2.4.1} & yes & no \\
-FIRST\underline{ }COMMENT\underline{ }NO\underline{ }SPACES & \hyperref[sec:2.4.1]{ 2.4.1} & yes & no \\
-COMMENT\underline{ }WHITE\underline{ }SPACE & \hyperref[sec:2.4.1]{ 2.4.1} & yes & maxSpaces \\
-IF\underline{ }ELSE\underline{ }COMMENTS & \hyperref[sec:2.4.1]{ 2.4.1} & yes & no \\
-COMMENTED\underline{ }OUT\underline{ }CODE & \hyperref[sec:2.4.2]{ 2.4.2} & no & no \\
-FILE\underline{ }IS\underline{ }TOO\underline{ }LONG & \hyperref[sec:3.1.1]{ 3.1.1} & no & maxSize ignoreFolders \\
-FILE\underline{ }CONTAINS\underline{ }ONLY\underline{ }COMMENTS & \hyperref[sec:3.1.2]{ 3.1.2} & no & no \\
-FILE\underline{ }INCORRECT\underline{ }BLOCKS\underline{ }ORDER & \hyperref[sec:3.1.2]{ 3.1.2} & yes & no \\
-FILE\underline{ }NO\underline{ }BLANK\underline{ }LINE\underline{ }BETWEEN\underline{ }BLOCKS & \hyperref[sec:3.1.2]{ 3.1.2} & yes & no \\
-FILE\underline{ }UNORDERED\underline{ }IMPORTS & \hyperref[sec:3.1.2]{ 3.1.2} & yes & no \\
-FILE\underline{ }WILDCARD\underline{ }IMPORTS & \hyperref[sec:3.1.2]{ 3.1.2} & no & allowedWildcards \\
-FILE\underline{ }NAME\underline{ }MATCH\underline{ }CLASS & \hyperref[sec:3.1.2]{ 3.1.2} & yes & no \\
-WRONG\underline{ }ORDER\underline{ }IN\underline{ }CLASS\underline{ }LIKE\underline{ }STRUCTURES & \hyperref[sec:3.1.4]{ 3.1.4} & yes & no \\
-BLANK\underline{ }LINE\underline{ }BETWEEN\underline{ }PROPERTIES & \hyperref[sec:3.1.4]{ 3.1.4} & yes & no \\
-WRONG\underline{ }DECLARATIONS\underline{ }ORDER & \hyperref[sec:3.1.4]{ 3.1.4} & yes & no \\
-NO\underline{ }BRACES\underline{ }IN\underline{ }CONDITIONALS\underline{ }AND\underline{ }LOOPS & \hyperref[sec:3.2.1]{ 3.2.1} & yes & no \\
-BRACES\underline{ }BLOCK\underline{ }STRUCTURE\underline{ }ERROR & \hyperref[sec:3.2.2]{ 3.2.2} & yes & openBraceNewline closeBraceNewline \\
-WRONG\underline{ }INDENTATION & \hyperref[sec:3.3.1]{ 3.3.1} & yes & extendedIndentOfParameters alignedParameters extendedIndentAfterOperators extendedIndentBeforeDot indentationSize \\
-EMPTY\underline{ }BLOCK\underline{ }STRUCTURE\underline{ }ERROR & \hyperref[sec:3.4.1]{ 3.4.1} & yes & allowEmptyBlocks styleEmptyBlockWithNewline \\
-LONG\underline{ }LINE & \hyperref[sec:3.5.1]{ 3.5.1} & yes & lineLength \\
-MORE\underline{ }THAN\underline{ }ONE\underline{ }STATEMENT\underline{ }PER\underline{ }LINE & \hyperref[sec:3.6.1]{ 3.6.1} & yes & no \\
-REDUNDANT\underline{ }SEMICOLON & \hyperref[sec:3.6.2]{ 3.6.2} & yes & no \\
-WRONG\underline{ }NEWLINES & \hyperref[sec:3.6.2]{ 3.6.2} & yes & no \\
-TOO\underline{ }MANY\underline{ }BLANK\underline{ }LINES & \hyperref[sec:3.7.1]{ 3.7.1} & yes & no \\
-WRONG\underline{ }WHITESPACE & \hyperref[sec:3.8.1]{ 3.8.1} & yes & no \\
-TOO\underline{ }MANY\underline{ }CONSECUTIVE\underline{ }SPACES & \hyperref[sec:3.8.1]{ 3.8.1} & yes & maxSpaces saveInitialFormattingForEnums \\
-ENUMS\underline{ }SEPARATED & \hyperref[sec:3.9.1]{ 3.9.1} & yes & no \\
-LOCAL\underline{ }VARIABLE\underline{ }EARLY\underline{ }DECLARATION & \hyperref[sec:3.10.2]{ 3.10.2} & no & no \\
-WHEN\underline{ }WITHOUT\underline{ }ELSE & \hyperref[sec:3.11.1]{ 3.11.1} & yes & no \\
-ANNOTATION\underline{ }NEW\underline{ }LINE & \hyperref[sec:3.12.1]{ 3.12.1} & yes & no \\
-WRONG\underline{ }MULTIPLE\underline{ }MODIFIERS\underline{ }ORDER & \hyperref[sec:3.14.1]{ 3.14.1} & yes & no \\
-LONG\underline{ }NUMERICAL\underline{ }VALUES\underline{ }SEPARATED & \hyperref[sec:3.14.2]{ 3.14.2} & yes & maxLength \\
-STRING\underline{ }CONCATENATION & \hyperref[sec:3.15.1]{ 3.15.1} & no & no \\
-STRING\underline{ }TEMPLATE\underline{ }CURLY\underline{ }BRACES & \hyperref[sec:3.15.2]{ 3.15.2} & yes & no \\
-STRING\underline{ }TEMPLATE\underline{ }QUOTES & \hyperref[sec:3.15.2]{ 3.15.2} & yes & no \\
-FLOAT\underline{ }IN\underline{ }ACCURATE\underline{ }CALCULATIONS & \hyperref[sec:4.1.1]{ 4.1.1} & no & no \\
-SAY\underline{ }NO\underline{ }TO\underline{ }VAR & \hyperref[sec:4.1.3]{ 4.1.3} & no & no \\
-SMART\underline{ }CAST\underline{ }NEEDED & \hyperref[sec:4.2.1]{ 4.2.1} & yes & no \\
-TYPE\underline{ }ALIAS & \hyperref[sec:4.2.2]{ 4.2.2} & no & typeReferenceLength \\
-NULLABLE\underline{ }PROPERTY\underline{ }TYPE & \hyperref[sec:4.3.1]{ 4.3.1} & yes & no \\
-GENERIC\underline{ }VARIABLE\underline{ }WRONG\underline{ }DECLARATION & \hyperref[sec:4.3.2]{ 4.3.2} & yes & no \\
-AVOID\underline{ }NULL\underline{ }CHECKS & \hyperref[sec:4.3.3]{ 4.3.3} & no & no \\
-TOO\underline{ }LONG\underline{ }FUNCTION & \hyperref[sec:5.1.1]{ 5.1.1} & no & maxFunctionLength isIncludeHeader\\
-NESTED\underline{ }BLOCK & \hyperref[sec:5.1.2]{ 5.1.2} & no & maxNestedBlockQuantit\\
-AVOID\underline{ }NESTED\underline{ }FUNCTIONS & \hyperref[sec:5.1.3]{ 5.1.3} & yes & no \\
-LAMBDA\underline{ }IS\underline{ }NOT\underline{ }LAST\underline{ }PARAMETER & \hyperref[sec:5.2.1]{ 5.2.1} & no & no \\
-TOO\underline{ }MANY\underline{ }PARAMETERS & \hyperref[sec:5.2.2]{ 5.2.2} & no & maxParameterListSize \\
-WRONG\underline{ }OVERLOADING\underline{ }FUNCTION\underline{ }ARGUMENTS & \hyperref[sec:5.2.3]{ 5.2.3} & no & no \\
-SINGLE\underline{ }CONSTRUCTOR\underline{ }SHOULD\underline{ }BE\underline{ }PRIMARY & \hyperref[sec:6.1.1]{ 6.1.1} & yes & no \\
-USE\underline{ }DATA\underline{ }CLASS & \hyperref[sec:6.1.2]{ 6.1.2} & no & no \\
-EMPTY\underline{ }PRIMARY\underline{ }CONSTRUCTOR & \hyperref[sec:6.1.3]{ 6.1.3} & yes & no \\
-MULTIPLE\underline{ }INIT\underline{ }BLOCKS & \hyperref[sec:6.1.4]{ 6.1.4} & yes & no \\
-USELESS\underline{ }SUPERTYPE & \hyperref[sec:6.1.5]{ 6.1.5} & yes & no \\
-CLASS\underline{ }SHOULD\underline{ }NOT\underline{ }BE\underline{ }ABSTRACT & \hyperref[sec:6.1.6]{ 6.1.6} & yes & no \\
-NO\underline{ }CORRESPONDING\underline{ }PROPERTY & \hyperref[sec:6.1.7]{ 6.1.7} & no & no \\
-CUSTOM\underline{ }GETTERS\underline{ }SETTERS & \hyperref[sec:6.1.8]{ 6.1.8} & no & no \\
-WRONG\underline{ }NAME\underline{ }OF\underline{ }VARIABLE\underline{ }INSIDE\underline{ }ACCESSOR & \hyperref[sec:6.1.9]{ 6.1.9} & no & no \\
-TRIVIAL\underline{ }ACCESSORS\underline{ }ARE\underline{ }NOT\underline{ }RECOMMENDED & \hyperref[sec:6.1.10]{ 6.1.10} & yes & no \\
-COMPACT\underline{ }OBJECT\underline{ }INITIALIZATION & \hyperref[sec:6.1.11]{ 6.1.11} & yes & no \\
-EXTENSION\underline{ }FUNCTION\underline{ }SAME\underline{ }SIGNATURE & \hyperref[sec:6.2.2]{ 6.2.2} & no & no \\
-AVOID\underline{ }USING\underline{ }UTILITY\underline{ }CLASS & \hyperref[sec:6.4.1]{ 6.4.1} & no & no \\
-OBJECT\underline{ }IS\underline{ }PREFERRED & \hyperref[sec:6.4.2]{ 6.4.2} & yes & no \\
+VARIABLE\underline{ }NAME\underline{ }INCORRECT & \hyperref[sec:1.1.1]{1.1.1} & no & no \\
+VARIABLE\underline{ }HAS\underline{ }PREFIX & \hyperref[sec:1.1.1]{1.1.1} & yes & no \\
+IDENTIFIER\underline{ }LENGTH & \hyperref[sec:1.1.1]{1.1.1} & no & no \\
+GENERIC\underline{ }NAME & \hyperref[sec:1.1.1]{1.1.1} & yes & no \\
+BACKTICKS\underline{ }PROHIBITED & \hyperref[sec:1.1.1]{1.1.1} & no & no \\
+FILE\underline{ }NAME\underline{ }INCORRECT & \hyperref[sec:1.1.1]{1.1.1} & yes & no \\
+EXCEPTION\underline{ }SUFFIX & \hyperref[sec:1.1.1]{1.1.1} & yes & no \\
+CONFUSING\underline{ }IDENTIFIER\underline{ }NAMING & \hyperref[sec:1.1.1]{1.1.1} & no & no \\
+PACKAGE\underline{ }NAME\underline{ }MISSING & \hyperref[sec:1.2.1]{1.2.1} & yes & no \\
+PACKAGE\underline{ }NAME\underline{ }INCORRECT\underline{ }CASE & \hyperref[sec:1.2.1]{1.2.1} & yes & no \\
+PACKAGE\underline{ }NAME\underline{ }INCORRECT\underline{ }PREFIX & \hyperref[sec:1.2.1]{1.2.1} & yes & no \\
+PACKAGE\underline{ }NAME\underline{ }INCORRECT\underline{ }SYMBOLS & \hyperref[sec:1.2.1]{1.2.1} & no & no \\
+PACKAGE\underline{ }NAME\underline{ }INCORRECT\underline{ }PATH & \hyperref[sec:1.2.1]{1.2.1} & yes & no \\
+INCORRECT\underline{ }PACKAGE\underline{ }SEPARATOR & \hyperref[sec:1.2.1]{1.2.1} & yes & no \\
+CLASS\underline{ }NAME\underline{ }INCORRECT & \hyperref[sec:1.3.1]{1.3.1} & yes & no \\
+OBJECT\underline{ }NAME\underline{ }INCORRECT & \hyperref[sec:1.3.1]{1.3.1} & yes & no \\
+ENUM\underline{ }VALUE & \hyperref[sec:1.3.1]{1.3.1} & yes & enumStyle: snakeCase, pascalCase \\
+FUNCTION\underline{ }NAME\underline{ }INCORRECT\underline{ }CASE & \hyperref[sec:1.4.1]{1.4.1} & yes & no \\
+CONSTANT\underline{ }UPPERCASE & \hyperref[sec:1.5.1]{1.5.1} & yes & no \\
+VARIABLE\underline{ }NAME\underline{ }INCORRECT\underline{ }FORMAT & \hyperref[sec:1.6.1]{1.6.1} & yes & no \\
+FUNCTION\underline{ }BOOLEAN\underline{ }PREFIX & \hyperref[sec:1.6.2]{1.6.2} & yes & no \\
+MISSING\underline{ }KDOC\underline{ }TOP\underline{ }LEVEL & \hyperref[sec:2.1.1]{2.1.1} & no & no \\
+MISSING\underline{ }KDOC\underline{ }CLASS\underline{ }ELEMENTS & \hyperref[sec:2.1.1]{2.1.1} & no & no \\
+MISSING\underline{ }KDOC\underline{ }ON\underline{ }FUNCTION & \hyperref[sec:2.1.1]{2.1.1} & yes & no \\
+KDOC\underline{ }NO\underline{ }CONSTRUCTOR\underline{ }PROPERTY & \hyperref[sec:2.1.1]{2.1.1} & yes & no \\
+KDOC\underline{ }EXTRA\underline{ }PROPERTY & \hyperref[sec:2.1.1]{2.1.1} & no & no \\
+KDOC\underline{ }NO\underline{ }CONSTRUCTOR\underline{ }PROPERTY\underline{ }WITH\underline{ }COMMENT & \hyperref[sec:2.1.1]{2.1.1} & yes & no \\
+KDOC\underline{ }WITHOUT\underline{ }PARAM\underline{ }TAG & \hyperref[sec:2.1.2]{2.1.2} & yes & no \\
+KDOC\underline{ }WITHOUT\underline{ }RETURN\underline{ }TAG & \hyperref[sec:2.1.2]{2.1.2} & yes & no \\
+KDOC\underline{ }WITHOUT\underline{ }THROWS\underline{ }TAG & \hyperref[sec:2.1.2]{2.1.2} & yes & no \\
+KDOC\underline{ }EMPTY\underline{ }KDOC & \hyperref[sec:2.1.3]{2.1.3} & no & no \\
+KDOC\underline{ }WRONG\underline{ }SPACES\underline{ }AFTER\underline{ }TAG & \hyperref[sec:2.1.3]{2.1.3} & yes & no \\
+KDOC\underline{ }WRONG\underline{ }TAGS\underline{ }ORDER & \hyperref[sec:2.1.3]{2.1.3} & yes & no \\
+KDOC\underline{ }NEWLINES\underline{ }BEFORE\underline{ }BASIC\underline{ }TAGS & \hyperref[sec:2.1.3]{2.1.3} & yes & no \\
+KDOC\underline{ }NO\underline{ }NEWLINES\underline{ }BETWEEN\underline{ }BASIC\underline{ }TAGS & \hyperref[sec:2.1.3]{2.1.3} & yes & no \\
+KDOC\underline{ }NO\underline{ }NEWLINE\underline{ }AFTER\underline{ }SPECIAL\underline{ }TAGS & \hyperref[sec:2.1.3]{2.1.3} & yes & no \\
+KDOC\underline{ }NO\underline{ }DEPRECATED\underline{ }TAG & \hyperref[sec:2.1.3]{2.1.3} & yes & no \\
+KDOC\underline{ }NO\underline{ }EMPTY\underline{ }TAGS & \hyperref[sec:2.2.1]{2.2.1} & no & no \\
+HEADER\underline{ }WRONG\underline{ }FORMAT & \hyperref[sec:2.2.1]{2.2.1} & yes & no \\
+HEADER\underline{ }MISSING\underline{ }OR\underline{ }WRONG\underline{ }COPYRIGHT & \hyperref[sec:2.2.1]{2.2.1} & yes & mandatoryCopyright \\
+WRONG\underline{ }COPYRIGHT\underline{ }YEAR & \hyperref[sec:2.2.1]{2.2.1} & yes & no \\
+HEADER\underline{ }CONTAINS\underline{ }DATE\underline{ }OR\underline{ }AUTHOR & \hyperref[sec:2.2.1]{2.2.1} & no & no \\
+HEADER\underline{ }MISSING\underline{ }IN\underline{ }NON\underline{ }SINGLE\underline{ }CLASS\underline{ }FILE & \hyperref[sec:2.2.1]{2.2.1} & no & no \\
+HEADER\underline{ }NOT\underline{ }BEFORE\underline{ }PACKAGE & \hyperref[sec:2.2.1]{2.2.1} & yes & no \\
+KDOC\underline{ }TRIVIAL\underline{ }KDOC\underline{ }ON\underline{ }FUNCTION & \hyperref[sec:2.3.1]{2.3.1} & no & no \\
+WRONG\underline{ }NEWLINES\underline{ }AROUND\underline{ }KDOC & \hyperref[sec:2.4.1]{2.4.1} & yes & no \\
+FIRST\underline{ }COMMENT\underline{ }NO\underline{ }SPACES & \hyperref[sec:2.4.1]{2.4.1} & yes & no \\
+COMMENT\underline{ }WHITE\underline{ }SPACE & \hyperref[sec:2.4.1]{2.4.1} & yes & maxSpaces \\
+IF\underline{ }ELSE\underline{ }COMMENTS & \hyperref[sec:2.4.1]{2.4.1} & yes & no \\
+COMMENTED\underline{ }OUT\underline{ }CODE & \hyperref[sec:2.4.2]{2.4.2} & no & no \\
+FILE\underline{ }IS\underline{ }TOO\underline{ }LONG & \hyperref[sec:3.1.1]{3.1.1} & no & maxSize ignoreFolders \\
+FILE\underline{ }CONTAINS\underline{ }ONLY\underline{ }COMMENTS & \hyperref[sec:3.1.2]{3.1.2} & no & no \\
+FILE\underline{ }INCORRECT\underline{ }BLOCKS\underline{ }ORDER & \hyperref[sec:3.1.2]{3.1.2} & yes & no \\
+FILE\underline{ }NO\underline{ }BLANK\underline{ }LINE\underline{ }BETWEEN\underline{ }BLOCKS & \hyperref[sec:3.1.2]{3.1.2} & yes & no \\
+FILE\underline{ }UNORDERED\underline{ }IMPORTS & \hyperref[sec:3.1.2]{3.1.2} & yes & no \\
+FILE\underline{ }WILDCARD\underline{ }IMPORTS & \hyperref[sec:3.1.2]{3.1.2} & no & allowedWildcards \\
+FILE\underline{ }NAME\underline{ }MATCH\underline{ }CLASS & \hyperref[sec:3.1.2]{3.1.2} & yes & no \\
+WRONG\underline{ }ORDER\underline{ }IN\underline{ }CLASS\underline{ }LIKE\underline{ }STRUCTURES & \hyperref[sec:3.1.4]{3.1.4} & yes & no \\
+BLANK\underline{ }LINE\underline{ }BETWEEN\underline{ }PROPERTIES & \hyperref[sec:3.1.4]{3.1.4} & yes & no \\
+WRONG\underline{ }DECLARATIONS\underline{ }ORDER & \hyperref[sec:3.1.4]{3.1.4} & yes & no \\
+NO\underline{ }BRACES\underline{ }IN\underline{ }CONDITIONALS\underline{ }AND\underline{ }LOOPS & \hyperref[sec:3.2.1]{3.2.1} & yes & no \\
+BRACES\underline{ }BLOCK\underline{ }STRUCTURE\underline{ }ERROR & \hyperref[sec:3.2.2]{3.2.2} & yes & openBraceNewline closeBraceNewline \\
+WRONG\underline{ }INDENTATION & \hyperref[sec:3.3.1]{3.3.1} & yes & extendedIndentOfParameters alignedParameters extendedIndentAfterOperators extendedIndentBeforeDot indentationSize \\
+EMPTY\underline{ }BLOCK\underline{ }STRUCTURE\underline{ }ERROR & \hyperref[sec:3.4.1]{3.4.1} & yes & allowEmptyBlocks styleEmptyBlockWithNewline \\
+LONG\underline{ }LINE & \hyperref[sec:3.5.1]{3.5.1} & yes & lineLength \\
+MORE\underline{ }THAN\underline{ }ONE\underline{ }STATEMENT\underline{ }PER\underline{ }LINE & \hyperref[sec:3.6.1]{3.6.1} & yes & no \\
+REDUNDANT\underline{ }SEMICOLON & \hyperref[sec:3.6.2]{3.6.2} & yes & no \\
+WRONG\underline{ }NEWLINES & \hyperref[sec:3.6.2]{3.6.2} & yes & no \\
+TOO\underline{ }MANY\underline{ }BLANK\underline{ }LINES & \hyperref[sec:3.7.1]{3.7.1} & yes & no \\
+WRONG\underline{ }WHITESPACE & \hyperref[sec:3.8.1]{3.8.1} & yes & no \\
+TOO\underline{ }MANY\underline{ }CONSECUTIVE\underline{ }SPACES & \hyperref[sec:3.8.1]{3.8.1} & yes & maxSpaces saveInitialFormattingForEnums \\
+ENUMS\underline{ }SEPARATED & \hyperref[sec:3.9.1]{3.9.1} & yes & no \\
+LOCAL\underline{ }VARIABLE\underline{ }EARLY\underline{ }DECLARATION & \hyperref[sec:3.10.2]{3.10.2} & no & no \\
+WHEN\underline{ }WITHOUT\underline{ }ELSE & \hyperref[sec:3.11.1]{3.11.1} & yes & no \\
+ANNOTATION\underline{ }NEW\underline{ }LINE & \hyperref[sec:3.12.1]{3.12.1} & yes & no \\
+WRONG\underline{ }MULTIPLE\underline{ }MODIFIERS\underline{ }ORDER & \hyperref[sec:3.14.1]{3.14.1} & yes & no \\
+LONG\underline{ }NUMERICAL\underline{ }VALUES\underline{ }SEPARATED & \hyperref[sec:3.14.2]{3.14.2} & yes & maxNumberLength maxBlockLength \\
+STRING\underline{ }CONCATENATION & \hyperref[sec:3.15.1]{3.15.1} & no & no \\
+STRING\underline{ }TEMPLATE\underline{ }CURLY\underline{ }BRACES & \hyperref[sec:3.15.2]{3.15.2} & yes & no \\
+STRING\underline{ }TEMPLATE\underline{ }QUOTES & \hyperref[sec:3.15.2]{3.15.2} & yes & no \\
+FLOAT\underline{ }IN\underline{ }ACCURATE\underline{ }CALCULATIONS & \hyperref[sec:4.1.1]{4.1.1} & no & no \\
+SAY\underline{ }NO\underline{ }TO\underline{ }VAR & \hyperref[sec:4.1.3]{4.1.3} & no & no \\
+SMART\underline{ }CAST\underline{ }NEEDED & \hyperref[sec:4.2.1]{4.2.1} & yes & no \\
+TYPE\underline{ }ALIAS & \hyperref[sec:4.2.2]{4.2.2} & no & typeReferenceLength \\
+NULLABLE\underline{ }PROPERTY\underline{ }TYPE & \hyperref[sec:4.3.1]{4.3.1} & yes & no \\
+GENERIC\underline{ }VARIABLE\underline{ }WRONG\underline{ }DECLARATION & \hyperref[sec:4.3.2]{4.3.2} & yes & no \\
+AVOID\underline{ }NULL\underline{ }CHECKS & \hyperref[sec:4.3.3]{4.3.3} & no & no \\
+TOO\underline{ }LONG\underline{ }FUNCTION & \hyperref[sec:5.1.1]{5.1.1} & no & maxFunctionLength isIncludeHeader \\
+NESTED\underline{ }BLOCK & \hyperref[sec:5.1.2]{5.1.2} & no & maxNestedBlockQuantit\\
+AVOID\underline{ }NESTED\underline{ }FUNCTIONS & \hyperref[sec:5.1.3]{5.1.3} & yes & no \\
+LAMBDA\underline{ }IS\underline{ }NOT\underline{ }LAST\underline{ }PARAMETER & \hyperref[sec:5.2.1]{5.2.1} & no & no \\
+TOO\underline{ }MANY\underline{ }PARAMETERS & \hyperref[sec:5.2.2]{5.2.2} & no & maxParameterListSize \\
+WRONG\underline{ }OVERLOADING\underline{ }FUNCTION\underline{ }ARGUMENTS & \hyperref[sec:5.2.3]{5.2.3} & no & no \\
+SINGLE\underline{ }CONSTRUCTOR\underline{ }SHOULD\underline{ }BE\underline{ }PRIMARY & \hyperref[sec:6.1.1]{6.1.1} & yes & no \\
+USE\underline{ }DATA\underline{ }CLASS & \hyperref[sec:6.1.2]{6.1.2} & no & no \\
+EMPTY\underline{ }PRIMARY\underline{ }CONSTRUCTOR & \hyperref[sec:6.1.3]{6.1.3} & yes & no \\
+MULTIPLE\underline{ }INIT\underline{ }BLOCKS & \hyperref[sec:6.1.4]{6.1.4} & yes & no \\
+USELESS\underline{ }SUPERTYPE & \hyperref[sec:6.1.5]{6.1.5} & yes & no \\
+CLASS\underline{ }SHOULD\underline{ }NOT\underline{ }BE\underline{ }ABSTRACT & \hyperref[sec:6.1.6]{6.1.6} & yes & no \\
+NO\underline{ }CORRESPONDING\underline{ }PROPERTY & \hyperref[sec:6.1.7]{6.1.7} & no & no \\
+CUSTOM\underline{ }GETTERS\underline{ }SETTERS & \hyperref[sec:6.1.8]{6.1.8} & no & no \\
+WRONG\underline{ }NAME\underline{ }OF\underline{ }VARIABLE\underline{ }INSIDE\underline{ }ACCESSOR & \hyperref[sec:6.1.9]{6.1.9} & no & no \\
+TRIVIAL\underline{ }ACCESSORS\underline{ }ARE\underline{ }NOT\underline{ }RECOMMENDED & \hyperref[sec:6.1.10]{6.1.10} & yes & no \\
+COMPACT\underline{ }OBJECT\underline{ }INITIALIZATION & \hyperref[sec:6.1.11]{6.1.11} & yes & no \\
+EXTENSION\underline{ }FUNCTION\underline{ }SAME\underline{ }SIGNATURE & \hyperref[sec:6.2.2]{6.2.2} & no & no \\
+AVOID\underline{ }USING\underline{ }UTILITY\underline{ }CLASS & \hyperref[sec:6.4.1]{6.4.1} & no & no \\
+OBJECT\underline{ }IS\underline{ }PREFERRED & \hyperref[sec:6.4.2]{6.4.2} & yes & no \\
\hline
\end{longtable}
\lstMakeShortInline[basicstyle=\ttfamily\bfseries]`
@@ -518,7 +518,7 @@ \subsubsection*{\textbf{1.1.1 Identifiers naming conventions}}
\begin{center}
-\begin{tabular}{ |p{5,0cm}|p{5,0cm}|p{5,0cm}| }
+\begin{tabular}{ |p{5.0cm}|p{5.0cm}|p{5.0cm}| }
\hline
@@ -564,7 +564,7 @@ \subsubsection*{\textbf{1.1.1 Identifiers naming conventions}}
\begin{center}
-\begin{tabular}{ |p{5,0cm}|p{5,0cm}|p{5,0cm}| }
+\begin{tabular}{ |p{5.0cm}|p{5.0cm}|p{5.0cm}| }
\hline
@@ -608,7 +608,7 @@ \subsubsection*{\textbf{1.1.1 Identifiers naming conventions}}
\begin{center}
-\begin{tabular}{ |p{7,5cm}|p{7,5cm}| }
+\begin{tabular}{ |p{7.5cm}|p{7.5cm}| }
\hline
@@ -2216,7 +2216,7 @@ \subsection*{\textbf{3.8 Horizontal space}}
This section describes general rules and recommendations for using spaces in the code.
-\subsubsection*{\textbf{3.8.1}}
+\subsubsection*{\textbf{3.8.1: Usage of whitespace for code separation}}
\leavevmode\newline
\label{sec:3.8.1}
@@ -2352,7 +2352,7 @@ \subsubsection*{\textbf{3.8.1}}
-\subsubsection*{\textbf{3.8.2}}
+\subsubsection*{\textbf{3.8.2: No spaces for horizontal alignment}}
\leavevmode\newline
\label{sec:3.8.2}
@@ -2634,7 +2634,7 @@ \subsubsection*{\textbf{3.14.1 Declaration with multiple modifiers}}
\end{lstlisting}
-\subsubsection*{\textbf{3.14.2}}
+\subsubsection*{\textbf{3.14.2: Separate long numerical values with an underscore}}
\leavevmode\newline
\label{sec:3.14.2}
@@ -2793,7 +2793,7 @@ \subsubsection*{\textbf{4.1.1 Do not use Float and Double types when accurate ca
\end{lstlisting}
-\subsubsection*{\textbf{4.1.2}}
+\subsubsection*{\textbf{4.1.2: Comparing numeric float type values}}
\leavevmode\newline
\label{sec:4.1.2}
@@ -2861,7 +2861,7 @@ \subsection*{\textbf{4.2 Types}}
This section provides recommendations for using types.
-\subsubsection*{\textbf{4.2.1}}
+\subsubsection*{\textbf{4.2.1: Use Contracts and smart cast as much as possible}}
\leavevmode\newline
\label{sec:4.2.1}
@@ -2931,7 +2931,7 @@ \subsubsection*{\textbf{4.2.1}}
\end{lstlisting}
-\subsubsection*{\textbf{4.2.2}}
+\subsubsection*{\textbf{4.2.2: Try to use type alias to represent types making code more readable}}
\leavevmode\newline
\label{sec:4.2.2}
@@ -2980,7 +2980,7 @@ \subsection*{\textbf{4.3 Null safety and variable declarations}}
-\subsubsection*{\textbf{4.3.1}}
+\subsubsection*{\textbf{4.3.1: Avoid declaring variables with nullable types}}
\leavevmode\newline
\label{sec:4.3.1}
@@ -3025,7 +3025,7 @@ \subsubsection*{\textbf{4.3.1}}
\end{lstlisting}
-\subsubsection*{\textbf{4.3.2}}
+\subsubsection*{\textbf{4.3.2: Variables of generic types should have an explicit type declaration}}
\leavevmode\newline
\label{sec:4.3.2}
From cea7a7a15667a5e75688b496daa01e4261a3722f Mon Sep 17 00:00:00 2001
From: Andrey Kuleshov
Date: Wed, 13 Jan 2021 15:21:25 +0300
Subject: [PATCH 02/11] Updates to the codestyle (#702)
Updates to the codestyle
### What's done:
Regenerated the codestyle
---
README.md | 2 +-
info/guide/diktat-coding-convention.md | 8 ++++++++
info/guide/guide-TOC.md | 3 +++
info/guide/guide-chapter-0.md | 5 +++++
wp/sections/appendix.tex | 16 ++++++++++++++++
5 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 314ee429e7..46cfa6a1c7 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ Now diKTat was already added to the lists of [static analysis tools](https://git
| | | | |
| --- | --- | --- | --- |
-|[DiKTat codestyle](info/guide/diktat-coding-convention.md)|[Supported Rules](info/available-rules.md) | [Examples of Usage](https://github.com/akuleshov7/diktat-examples) | [Online Demo](https://ktlint-demo.herokuapp.com) |
+|[DiKTat codestyle](info/guide/diktat-coding-convention.md)|[Supported Rules](info/available-rules.md) | [Examples of Usage](https://github.com/akuleshov7/diktat-examples) | [Online Demo](https://ktlint-demo.herokuapp.com) | [Whitepaper](wp/wp.pdf) |
## Why should I use diktat in my CI/CD?
diff --git a/info/guide/diktat-coding-convention.md b/info/guide/diktat-coding-convention.md
index e6e3fbde56..0450dc2531 100644
--- a/info/guide/diktat-coding-convention.md
+++ b/info/guide/diktat-coding-convention.md
@@ -1,3 +1,6 @@
+
+
+# Diktat Coding Style Guide, v.0.0.1
# Table of contents
I [Preface](#c0)
@@ -107,6 +110,11 @@ I [Preface](#c0)
* [6.4.2 Objects should be used for Stateless Interfaces](#r6.4.2)
+# Diktat Coding Style Guide
+# International version, v.0.0.1
+
+
+
## Preface
### Purpose of this document
diff --git a/info/guide/guide-TOC.md b/info/guide/guide-TOC.md
index ec5ef6e66d..524e30dad2 100644
--- a/info/guide/guide-TOC.md
+++ b/info/guide/guide-TOC.md
@@ -1,3 +1,6 @@
+
+
+# Diktat Coding Style Guide, v.0.0.1
# Table of contents
I [Preface](#c0)
diff --git a/info/guide/guide-chapter-0.md b/info/guide/guide-chapter-0.md
index 7d010dbacf..54774996a7 100644
--- a/info/guide/guide-chapter-0.md
+++ b/info/guide/guide-chapter-0.md
@@ -1,3 +1,8 @@
+# Diktat Coding Style Guide
+# International version, v.0.0.1
+
+
+
## Preface
### Purpose of this document
diff --git a/wp/sections/appendix.tex b/wp/sections/appendix.tex
index ef8f2b9eae..e7c8fbcddc 100644
--- a/wp/sections/appendix.tex
+++ b/wp/sections/appendix.tex
@@ -127,6 +127,12 @@ \section*{Available Rules}
\end{longtable}
\lstMakeShortInline[basicstyle=\ttfamily\bfseries]`
+\hspace{0.0cm}\hyperref[sec:]{}
+
+\section*{Diktat Coding Style Guide, v.0.0.1}
+
+\hspace{0.0cm}\hyperref[sec:]{}
+
\section*{Table of contents}
\hspace{0.0cm}\hyperref[sec:]{I Preface}
@@ -343,6 +349,16 @@ \section*{Table of contents}
\hspace{0.0cm}\hyperref[sec:]{}
+\section*{Diktat Coding Style Guide}
+
+\section*{International version, v.0.0.1}
+
+\hspace{0.0cm}\hyperref[sec:]{}
+
+\hspace{0.0cm}\hyperref[sec:]{}
+
+\hspace{0.0cm}\hyperref[sec:]{}
+
\subsection*{\textbf{Purpose of this document}}
\label{sec:}
From 5cb0ce501c185d444416dad1aa2f3a4d14446478 Mon Sep 17 00:00:00 2001
From: Peter Trifanov
Date: Wed, 13 Jan 2021 17:59:48 +0300
Subject: [PATCH 03/11] Minor infrastructure improvements (#700)
### What's done:
* Fixed kotlin compiler warnings
* Updated version in build.gradle.kts for diktat-gradle-plugin
* Added update of info/buildSrc/gradle.properties version during release workflow
* Run gradle functional tests in parallel
* One-phase build in build_and_test.yml instead of two
---
.github/workflows/build_and_test.yml | 4 +---
.github/workflows/release.yml | 2 ++
diktat-gradle-plugin/build.gradle.kts | 3 ++-
diktat-rules/pom.xml | 1 +
.../diktat/ruleset/rules/identifiers/LocalVariablesRule.kt | 6 +++---
.../kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 4 ++--
.../org/cqfn/diktat/ruleset/utils/search/VariablesSearch.kt | 2 +-
.../ruleset/chapter3/spaces/WhiteSpaceRuleWarnTest.kt | 2 +-
.../diktat/ruleset/utils/VariablesWithUsagesSearchTest.kt | 1 -
9 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml
index 6dbc1c6113..5f3983c7f5 100644
--- a/.github/workflows/build_and_test.yml
+++ b/.github/workflows/build_and_test.yml
@@ -22,10 +22,8 @@ jobs:
key: ${{ runner.os }}-maven-build-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-build-
- - name: Maven Package
- run: mvn -B clean package -DskipTests
- name: Maven Install
- run: mvn -B install
+ run: mvn -B clean install
- name: Code coverage report
uses: codecov/codecov-action@v1
with:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c2260a5bbb..f864cb1cd6 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -78,6 +78,8 @@ jobs:
sed -i "s/$PREVIOUS_VERSION/$RELEASE_VERSION/g" $file || echo "File $file hasn't been updated!"
cp diktat-rules/src/main/resources/diktat-analysis.yml $(dirname $file)
done
+ next_snapshot_version=$(printf 'VERSION=${project.version}\n0\n' | mvn help:evaluate | grep '^VERSION' | cut -d= -f2)
+ echo "version=$next_snapshot_version" > info/buildSrc/gradle.properties
- name: Create pull request
uses: peter-evans/create-pull-request@v3
with:
diff --git a/diktat-gradle-plugin/build.gradle.kts b/diktat-gradle-plugin/build.gradle.kts
index 08f576145d..5bf9b08466 100644
--- a/diktat-gradle-plugin/build.gradle.kts
+++ b/diktat-gradle-plugin/build.gradle.kts
@@ -20,7 +20,7 @@ repositories {
// default value is needed for correct gradle loading in IDEA; actual value from maven is used during build
val ktlintVersion = project.properties.getOrDefault("ktlintVersion", "0.39.0") as String
-val diktatVersion = project.version.takeIf { it.toString() != Project.DEFAULT_VERSION } ?: "0.2.0"
+val diktatVersion = project.version.takeIf { it.toString() != Project.DEFAULT_VERSION } ?: "0.3.0"
val junitVersion = project.properties.getOrDefault("junitVersion", "5.7.0") as String
val jacocoVersion = project.properties.getOrDefault("jacocoVersion", "0.8.6") as String
dependencies {
@@ -98,6 +98,7 @@ tasks.getByName("functionalTest") {
dependsOn("test")
testClassesDirs = functionalTest.output.classesDirs
classpath = functionalTest.runtimeClasspath
+ maxParallelForks = Runtime.getRuntime().availableProcessors()
doLast {
if (getCurrentOperatingSystem().isWindows) {
// workaround for https://github.com/koral--/jacoco-gradle-testkit-plugin/issues/9
diff --git a/diktat-rules/pom.xml b/diktat-rules/pom.xml
index 2c166e9909..280f6d6ceb 100644
--- a/diktat-rules/pom.xml
+++ b/diktat-rules/pom.xml
@@ -110,6 +110,7 @@
+
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 dbcd5a8061..9c4676ccf7 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
@@ -91,7 +91,7 @@ class LocalVariablesRule(private val configRules: List) : Rule("loc
val declarationScope = property.getDeclarationScope()
val firstUsageStatementLine = getFirstUsageStatementOrBlock(usages, declarationScope).node.getLineNumber()
- val firstUsage = usages.minBy { it.node.getLineNumber() }!!
+ val firstUsage = usages.minByOrNull { it.node.getLineNumber() }!!
// should skip val and var before it's statement
val offset = property
@@ -184,7 +184,7 @@ class LocalVariablesRule(private val configRules: List) : Rule("loc
*/
@Suppress("UnsafeCallOnNullableType", "GENERIC_VARIABLE_WRONG_DECLARATION")
private fun getFirstUsageStatementOrBlock(usages: List, declarationScope: KtBlockExpression?): PsiElement {
- val firstUsage = usages.minBy { it.node.getLineNumber() }!!
+ val firstUsage = usages.minByOrNull { it.node.getLineNumber() }!!
val firstUsageScope = firstUsage.getParentOfType(true)
return if (firstUsageScope == declarationScope) {
@@ -194,7 +194,7 @@ class LocalVariablesRule(private val configRules: List) : Rule("loc
.find { it.parent == declarationScope }!!
} else {
// first usage is in deeper block compared to declaration, need to check how close is declaration to the first line of the block
- usages.minBy { it.node.getLineNumber() }!!
+ usages.minByOrNull { it.node.getLineNumber() }!!
.parentsWithSelf
.find { it.parent == declarationScope }!!
}
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 9acba46866..157dddcb5f 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
@@ -603,7 +603,7 @@ fun ASTNode.isChildAfterAnother(child: ASTNode, afterChild: ASTNode): Boolean =
* @return boolean result
*/
fun ASTNode.isChildAfterGroup(child: ASTNode, group: List): Boolean =
- getChildren(null).indexOf(child) > (group.map { getChildren(null).indexOf(it) }.max() ?: 0)
+ getChildren(null).indexOf(child) > (group.map { getChildren(null).indexOf(it) }.maxOrNull() ?: 0)
/**
* Checks whether [child] is before [beforeChild] among the children of [this] node
@@ -637,7 +637,7 @@ fun ASTNode.areChildrenBeforeChild(children: List, beforeChild: ASTNode
@Suppress("UnsafeCallOnNullableType")
fun ASTNode.areChildrenBeforeGroup(children: List, group: List): Boolean {
require(children.isNotEmpty() && group.isNotEmpty()) { "no sense to operate on empty lists" }
- return children.map { getChildren(null).indexOf(it) }.max()!! < group.map { getChildren(null).indexOf(it) }.min()!!
+ return children.map { getChildren(null).indexOf(it) }.maxOrNull()!! < group.map { getChildren(null).indexOf(it) }.minOrNull()!!
}
/**
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/search/VariablesSearch.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/search/VariablesSearch.kt
index e873f1dd83..fbc1789e8d 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/search/VariablesSearch.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/search/VariablesSearch.kt
@@ -110,5 +110,5 @@ abstract class VariablesSearch(val node: ASTNode, private val filterForVariables
*
* @param node an [ASTNode]
*/
-@SuppressWarnings("FunctionOnlyReturningConstant")
+@Suppress("UNUSED_PARAMETER", "FunctionOnlyReturningConstant")
fun default(node: KtProperty) = true
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/WhiteSpaceRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/WhiteSpaceRuleWarnTest.kt
index ad68718a6b..04e850c62f 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/WhiteSpaceRuleWarnTest.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/WhiteSpaceRuleWarnTest.kt
@@ -351,7 +351,7 @@ class WhiteSpaceRuleWarnTest : LintTestBase(::WhiteSpaceRule) {
@Test
@Tag(WarningNames.WRONG_WHITESPACE)
- fun `there should be no space before ? in nullable types`() {
+ fun `there should be no space before question mark in nullable types`() {
lintMethod(
"""
|class Example {
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/VariablesWithUsagesSearchTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/VariablesWithUsagesSearchTest.kt
index d0369f181c..5264de2399 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/VariablesWithUsagesSearchTest.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/utils/VariablesWithUsagesSearchTest.kt
@@ -332,7 +332,6 @@ class VariablesWithUsagesSearchTest {
val var1 = keys.elementAt(0)
val var2 = keys.elementAt(1)
val var3 = keys.elementAt(2)
- val var4 = keys.elementAt(3)
assertEquals("var v = 0", var1)
assertEquals(0, vars[var1]?.size)
assertEquals("var v = 1", var2)
From 9b295417736b23a8ed34374514558ce3671c5031 Mon Sep 17 00:00:00 2001
From: Peter Trifanov
Date: Thu, 14 Jan 2021 12:39:55 +0300
Subject: [PATCH 04/11] Check whether @since tag contains date or version
(#694)
### What's done:
* Added logic
* Added tests
* Renamed warning
* Updated docs on chapter 2
* Pass version regex via configuration
---
diktat-analysis.yml | 8 +-
.../src/main/kotlin/generated/WarningNames.kt | 4 +-
.../cqfn/diktat/ruleset/constants/Warnings.kt | 2 +-
.../diktat/ruleset/rules/IdentifierNaming.kt | 2 +-
.../rules/comments/HeaderCommentRule.kt | 20 ++--
.../ruleset/rules/kdoc/KdocFormatting.kt | 67 +++++++++++++
.../main/resources/diktat-analysis-huawei.yml | 8 +-
.../src/main/resources/diktat-analysis.yml | 8 +-
.../ruleset/chapter2/HeaderCommentRuleTest.kt | 22 +----
.../ruleset/chapter2/KdocFormattingTest.kt | 97 +++++++++++++++++++
.../gradle-groovy-dsl/diktat-analysis.yml | 6 +-
.../diktat-analysis.yml | 4 +-
.../gradle-kotlin-dsl/diktat-analysis.yml | 6 +-
examples/maven/diktat-analysis.yml | 6 +-
info/available-rules.md | 8 +-
info/guide/guide-chapter-2.md | 39 ++++++--
16 files changed, 236 insertions(+), 71 deletions(-)
diff --git a/diktat-analysis.yml b/diktat-analysis.yml
index d6f4e18929..c139b4c94b 100644
--- a/diktat-analysis.yml
+++ b/diktat-analysis.yml
@@ -115,6 +115,11 @@
# Checks that KDoc does not contain single line with words 'return', 'get' or 'set'
- name: KDOC_TRIVIAL_KDOC_ON_FUNCTION
enabled: true
+# Checks that kdoc does not contain @author tag or date
+- name: KDOC_CONTAINS_DATE_OR_AUTHOR
+ enabled: true
+ configuration:
+ versionRegex: \d+\.\d+\.\d+[-.\w\d]*
# Checks that there is newline after header KDoc
- name: HEADER_WRONG_FORMAT
enabled: true
@@ -130,9 +135,6 @@
# Checks that header kdoc is located before package directive
- name: HEADER_NOT_BEFORE_PACKAGE
enabled: true
-# Checks that header kdoc does not contain @author tag ar date
-- name: HEADER_CONTAINS_DATE_OR_AUTHOR
- enabled: true
# Checks that file does not contain lines > maxSize
- name: FILE_IS_TOO_LONG
enabled: true
diff --git a/diktat-rules/src/main/kotlin/generated/WarningNames.kt b/diktat-rules/src/main/kotlin/generated/WarningNames.kt
index 10842cf258..826f16ba87 100644
--- a/diktat-rules/src/main/kotlin/generated/WarningNames.kt
+++ b/diktat-rules/src/main/kotlin/generated/WarningNames.kt
@@ -86,14 +86,14 @@ public object WarningNames {
public const val KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT: String =
"KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT"
+ public const val KDOC_CONTAINS_DATE_OR_AUTHOR: String = "KDOC_CONTAINS_DATE_OR_AUTHOR"
+
public const val HEADER_WRONG_FORMAT: String = "HEADER_WRONG_FORMAT"
public const val HEADER_MISSING_OR_WRONG_COPYRIGHT: String = "HEADER_MISSING_OR_WRONG_COPYRIGHT"
public const val WRONG_COPYRIGHT_YEAR: String = "WRONG_COPYRIGHT_YEAR"
- public const val HEADER_CONTAINS_DATE_OR_AUTHOR: String = "HEADER_CONTAINS_DATE_OR_AUTHOR"
-
public const val HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE: String =
"HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE"
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 156d662dae..0d43046e44 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
@@ -67,10 +67,10 @@ enum class Warnings(
KDOC_NO_CONSTRUCTOR_PROPERTY(true, "2.1.1", "all properties from the primary constructor should be documented in a @property tag in KDoc"),
KDOC_EXTRA_PROPERTY(false, "2.1.1", "There is property in KDoc which is not present in the class"),
KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT(true, "2.1.1", "replace comment before property with @property tag in class KDoc"),
+ KDOC_CONTAINS_DATE_OR_AUTHOR(false, "2.1.3", "KDoc should not contain creation date and author name"),
HEADER_WRONG_FORMAT(true, "2.2.1", "file header comments should be properly formatted"),
HEADER_MISSING_OR_WRONG_COPYRIGHT(true, "2.2.1", "file header comment must include copyright information inside a block comment"),
WRONG_COPYRIGHT_YEAR(true, "2.2.1", "year defined in copyright and current year are different"),
- HEADER_CONTAINS_DATE_OR_AUTHOR(false, "2.2.1", "file header comment should not contain creation date and author name"),
HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE(false, "2.2.1", "files that contain multiple or no classes should contain description of what is inside of this file"),
HEADER_NOT_BEFORE_PACKAGE(true, "2.2.1", "header KDoc should be placed before package and imports"),
COMMENTED_OUT_CODE(false, "2.4.2", "you should not comment out code, use VCS to save it in history and delete this block"),
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 cb37fd2937..a393f46bcc 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
@@ -451,7 +451,7 @@ class IdentifierNaming(private val configRules: List) : Rule("ident
// FixMe: this should be moved to properties
val oneCharIdentifiers = setOf("i", "j", "k", "x", "y", "z")
- val booleanMethodPrefixes = setOf("has", "is", "are", "have", "should")
+ val booleanMethodPrefixes = setOf("has", "is", "are", "have", "should", "can")
val confusingIdentifierNames = setOf("O", "D", "I", "l", "Z", "S", "e", "B", "h", "n", "m", "rn")
}
}
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 d31bc93ee3..ffe278ba89 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
@@ -4,17 +4,18 @@ import org.cqfn.diktat.common.config.rules.RuleConfiguration
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.common.config.rules.getRuleConfig
import org.cqfn.diktat.ruleset.constants.EmitType
-import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_CONTAINS_DATE_OR_AUTHOR
import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE
import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_MISSING_OR_WRONG_COPYRIGHT
import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_NOT_BEFORE_PACKAGE
import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_WRONG_FORMAT
+import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR
import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_COPYRIGHT_YEAR
import org.cqfn.diktat.ruleset.utils.copyrightWords
import org.cqfn.diktat.ruleset.utils.findChildAfter
import org.cqfn.diktat.ruleset.utils.findChildBefore
import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType
import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
+import org.cqfn.diktat.ruleset.utils.kDocTags
import org.cqfn.diktat.ruleset.utils.moveChildBefore
import com.pinterest.ktlint.core.Rule
@@ -29,8 +30,12 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement
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.kdoc.parser.KDocKnownTag
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import java.time.LocalDate
+import java.time.format.DateTimeFormatter
+import java.time.temporal.ChronoField
/**
* Visitor for header comment in .kt file:
@@ -59,18 +64,7 @@ class HeaderCommentRule(private val configRules: List) : Rule("head
}
private fun checkHeaderKdoc(node: ASTNode) {
- val headerKdoc = node.findChildBefore(PACKAGE_DIRECTIVE, KDOC)
- headerKdoc?.let {
- // fixme we should also check date of creation, but it can be in different formats
- headerKdoc
- .text
- .split('\n')
- .filter { it.contains("@author") }
- .forEach {
- HEADER_CONTAINS_DATE_OR_AUTHOR.warn(configRules, emitWarn, isFixMode,
- it.trim(), headerKdoc.startOffset, headerKdoc)
- }
-
+ node.findChildBefore(PACKAGE_DIRECTIVE, KDOC)?.let { headerKdoc ->
if (headerKdoc.treeNext != null && headerKdoc.treeNext.elementType == WHITE_SPACE &&
headerKdoc.treeNext.text.count { it == '\n' } != 2) {
HEADER_WRONG_FORMAT.warnAndFix(configRules, emitWarn, isFixMode,
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 1696aa5e62..fefdfe34cc 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
@@ -1,7 +1,10 @@
package org.cqfn.diktat.ruleset.rules.kdoc
+import org.cqfn.diktat.common.config.rules.RuleConfiguration
import org.cqfn.diktat.common.config.rules.RulesConfig
+import org.cqfn.diktat.common.config.rules.getRuleConfig
import org.cqfn.diktat.ruleset.constants.EmitType
+import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_EMPTY_KDOC
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NEWLINES_BEFORE_BASIC_TAGS
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NO_DEPRECATED_TAG
@@ -43,6 +46,9 @@ import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.psiUtil.startOffset
+import java.time.format.DateTimeFormatter
+import java.time.temporal.ChronoField
+
/**
* Formatting visitor for Kdoc:
* 1) removing all blank lines between Kdoc and the code it's declaring
@@ -50,12 +56,15 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
* 3) ensuring there is only one white space between tag and it's body
* 4) ensuring tags @apiNote, @implSpec, @implNote have one empty line after their body
* 5) ensuring tags @param, @return, @throws are arranged in this order
+ * 6) ensuring @author tag is not used
+ * 7) ensuring @since tag contains only versions and not dates
*/
@Suppress("ForbiddenComment")
class KdocFormatting(private val configRules: List) : Rule("kdoc-formatting") {
private val basicTagsList = listOf(KDocKnownTag.PARAM, KDocKnownTag.RETURN, KDocKnownTag.THROWS)
private val specialTagNames = setOf("implSpec", "implNote", "apiNote")
private var isFixMode: Boolean = false
+ private var versionRegex: Regex? = null
private lateinit var emitWarn: EmitType
/**
@@ -69,6 +78,13 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo
isFixMode = autoCorrect
emitWarn = emit
+ versionRegex ?: run {
+ versionRegex = KdocFormatConfiguration(
+ configRules.getRuleConfig(KDOC_CONTAINS_DATE_OR_AUTHOR)?.configuration ?: emptyMap()
+ )
+ .versionRegex
+ }
+
if (node.elementType == KDOC && isKdocNotEmpty(node)) {
checkNoDeprecatedTag(node)
checkEmptyTags(node.kDocTags())
@@ -77,6 +93,7 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo
node.kDocBasicTags()?.let { checkEmptyLinesBetweenBasicTags(it) }
checkBasicTagsOrder(node)
checkNewLineAfterSpecialTags(node)
+ checkAuthorAndDate(node)
}
}
@@ -307,6 +324,17 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo
}
}
+ private fun checkAuthorAndDate(node: ASTNode) {
+ node.kDocTags()
+ ?.filter {
+ it.knownTag == KDocKnownTag.AUTHOR ||
+ it.knownTag == KDocKnownTag.SINCE && it.hasInvalidVersion()
+ }
+ ?.forEach {
+ KDOC_CONTAINS_DATE_OR_AUTHOR.warn(configRules, emitWarn, isFixMode, it.text.trim(), it.startOffset, it.node)
+ }
+ }
+
// fixme this method can be improved and extracted to utils
private fun ASTNode.hasEmptyLineAfter(): Boolean {
require(this.elementType == KDOC_TAG) { "This check is only for KDOC_TAG" }
@@ -321,4 +349,43 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo
private fun ASTNode.applyToPrevSibling(elementType: IElementType, consumer: ASTNode.() -> Unit) {
prevSibling { it.elementType == elementType }?.apply(consumer)
}
+
+ /**
+ * Checks whether this tag's content represents an invalid version
+ */
+ private fun KDocTag.hasInvalidVersion(): Boolean {
+ val content = getContent().trim()
+ if (' ' in content || '/' in content) {
+ // Filter based on symbols that are not allowed in versions. Assuming that people put either version or date in `@since` tag.
+ return true
+ }
+ return versionRegex?.matches(content)?.not()
+ ?: dateFormats.mapNotNull {
+ // try to parse using some standard date patterns
+ runCatching {
+ it.parse(content).get(ChronoField.YEAR)
+ }
+ .getOrNull()
+ }
+ .isNotEmpty()
+ }
+
+ /**
+ * A [RuleConfiguration] for KDoc formatting
+ */
+ class KdocFormatConfiguration(config: Map) : RuleConfiguration(config) {
+ /**
+ * Regular expression, if present, against which a version should be matched in `@since` tag.
+ */
+ val versionRegex: Regex? by lazy {
+ config["versionRegex"]?.let { Regex(it) }
+ }
+ }
+
+ companion object {
+ val dateFormats: List = listOf("yyyy-dd-mm", "yy-dd-mm", "yyyy-mm-dd", "yy-mm-dd", "yyyy.mm.dd", "yyyy.dd.mm")
+ .map {
+ DateTimeFormatter.ofPattern(it)
+ }
+ }
}
diff --git a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml
index 2681f665db..dc4b98a7df 100644
--- a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml
+++ b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml
@@ -112,6 +112,11 @@
# Checks that special tags `@apiNote`, `@implNote`, `@implSpec` have exactly one empty line after
- name: KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS
enabled: true
+# Checks that kdoc does not contain @author tag or date
+- name: KDOC_CONTAINS_DATE_OR_AUTHOR
+ enabled: true
+ configuration:
+ versionRegex: \d+\.\d+\.\d+[-.\w\d]*
# Checks that KDoc does not contain single line with words 'return', 'get' or 'set'
- name: KDOC_TRIVIAL_KDOC_ON_FUNCTION
enabled: true
@@ -130,9 +135,6 @@
# Checks that header kdoc is located before package directive
- name: HEADER_NOT_BEFORE_PACKAGE
enabled: true
-# Checks that header kdoc does not contain @author tag ar date
-- name: HEADER_CONTAINS_DATE_OR_AUTHOR
- enabled: true
# Checks that file does not contain lines > maxSize
- name: FILE_IS_TOO_LONG
enabled: true
diff --git a/diktat-rules/src/main/resources/diktat-analysis.yml b/diktat-rules/src/main/resources/diktat-analysis.yml
index cf353c0ce0..0c9e986f18 100644
--- a/diktat-rules/src/main/resources/diktat-analysis.yml
+++ b/diktat-rules/src/main/resources/diktat-analysis.yml
@@ -111,6 +111,11 @@
# Checks that special tags `@apiNote`, `@implNote`, `@implSpec` have exactly one empty line after
- name: KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS
enabled: true
+# Checks that kdoc does not contain @author tag or date
+- name: KDOC_CONTAINS_DATE_OR_AUTHOR
+ enabled: true
+ configuration:
+ versionRegex: \d+\.\d+\.\d+[-.\w\d]*
# Checks that KDoc does not contain single line with words 'return', 'get' or 'set'
- name: KDOC_TRIVIAL_KDOC_ON_FUNCTION
enabled: true
@@ -129,9 +134,6 @@
# Checks that header kdoc is located before package directive
- name: HEADER_NOT_BEFORE_PACKAGE
enabled: true
-# Checks that header kdoc does not contain @author tag ar date
-- name: HEADER_CONTAINS_DATE_OR_AUTHOR
- enabled: true
# Checks that file does not contain lines > maxSize
- name: FILE_IS_TOO_LONG
enabled: true
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt
index a427a349f8..52493bbdfd 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt
@@ -2,11 +2,11 @@ package org.cqfn.diktat.ruleset.chapter2
import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings
-import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_CONTAINS_DATE_OR_AUTHOR
import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE
import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_MISSING_OR_WRONG_COPYRIGHT
import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_NOT_BEFORE_PACKAGE
import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_WRONG_FORMAT
+import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR
import org.cqfn.diktat.ruleset.rules.DIKTAT_RULE_SET_ID
import org.cqfn.diktat.ruleset.rules.comments.HeaderCommentRule
import org.cqfn.diktat.util.LintTestBase
@@ -256,26 +256,6 @@ class HeaderCommentRuleTest : LintTestBase(::HeaderCommentRule) {
)
}
- @Test
- @Tag(WarningNames.HEADER_CONTAINS_DATE_OR_AUTHOR)
- fun `@author tag is not allowed in header comment`() {
- lintMethod(
- """
- |$copyrightBlock
- |/**
- | * Description of this file
- | * @author anonymous
- | */
- |
- |package org.cqfn.diktat.example
- |
- |class Example { }
- """.trimMargin(),
- LintError(4, 1, ruleId, "${HEADER_CONTAINS_DATE_OR_AUTHOR.warnText()} * @author anonymous"),
- rulesConfigList = rulesConfigList
- )
- }
-
@Test
@Tag(WarningNames.HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE)
fun `file with zero classes should have header KDoc`() {
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/KdocFormattingTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/KdocFormattingTest.kt
index 732797b566..02f2f092cb 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/KdocFormattingTest.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/KdocFormattingTest.kt
@@ -1,5 +1,7 @@
package org.cqfn.diktat.ruleset.chapter2
+import org.cqfn.diktat.common.config.rules.RulesConfig
+import org.cqfn.diktat.ruleset.constants.Warnings
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_EMPTY_KDOC
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NEWLINES_BEFORE_BASIC_TAGS
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NO_DEPRECATED_TAG
@@ -321,4 +323,99 @@ class KdocFormattingTest : LintTestBase(::KdocFormatting) {
LintError(2, 16, ruleId,
"${KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS.warnText()} @implSpec, @apiNote, @implNote", true))
}
+
+ @Test
+ @Tag(WarningNames.KDOC_CONTAINS_DATE_OR_AUTHOR)
+ fun `@author tag is not allowed in header comment`() {
+ lintMethod(
+ """
+ |/**
+ | * Description of this file
+ | * @author anonymous
+ | */
+ |
+ |package org.cqfn.diktat.example
+ |
+ |/**
+ | * Description of this class
+ | * @author anonymous
+ | */
+ |class Example { }
+ """.trimMargin(),
+ LintError(3, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @author anonymous"),
+ LintError(10, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @author anonymous"),
+ )
+ }
+
+ @Test
+ @Tag(WarningNames.KDOC_CONTAINS_DATE_OR_AUTHOR)
+ fun `@since tag should only contain versions`() {
+ lintMethod(
+ """
+ |/**
+ | * Description of this file
+ | * @since 2019-10-11
+ | * @since 19-10-11
+ | * @since 2019.10.11
+ | * @since 2019/10/11
+ | * @since 11 Oct 2019
+ | * @since 1.2.3
+ | * @since 1.2.3-1
+ | * @since 1.2.3-SNAPSHOT
+ | * @since 1.2.3-rc-1
+ | * @since 1.2.3.RELEASE
+ | */
+ |
+ |package org.cqfn.diktat.example
+ |
+ |/**
+ | * Description of this file
+ | * @since 2019-10-11
+ | * @since 1.2.3
+ | */
+ |class Example { }
+ """.trimMargin(),
+ LintError(3, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 2019-10-11"),
+ LintError(4, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 19-10-11"),
+ LintError(5, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 2019.10.11"),
+ LintError(6, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 2019/10/11"),
+ LintError(7, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 11 Oct 2019"),
+ LintError(19, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 2019-10-11"),
+ rulesConfigList = emptyList()
+ )
+ }
+
+ @Test
+ @Tag(WarningNames.KDOC_CONTAINS_DATE_OR_AUTHOR)
+ fun `@since tag should only contain versions - with configured regex`() {
+ lintMethod(
+ """
+ |/**
+ | * Description of this file
+ | * @since 2019-10-11
+ | * @since 1.2.3-rc-1
+ | * @since 1.2.3.RELEASE
+ | */
+ |
+ |package org.cqfn.diktat.example
+ |
+ |/**
+ | * Description of this file
+ | * @since 2019-10-11
+ | * @since 1.2.3
+ | */
+ |class Example { }
+ """.trimMargin(),
+ LintError(3, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 2019-10-11"),
+ LintError(5, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 1.2.3.RELEASE"),
+ LintError(12, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 2019-10-11"),
+ rulesConfigList = listOf(
+ RulesConfig(
+ Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.name, true, mapOf(
+ "versionRegex" to "\\d+\\.\\d+\\.\\d+[-\\w\\d]*"
+ )
+ )
+ )
+ )
+ }
}
diff --git a/examples/gradle-groovy-dsl/diktat-analysis.yml b/examples/gradle-groovy-dsl/diktat-analysis.yml
index cf353c0ce0..e3fcb8b435 100644
--- a/examples/gradle-groovy-dsl/diktat-analysis.yml
+++ b/examples/gradle-groovy-dsl/diktat-analysis.yml
@@ -111,6 +111,9 @@
# Checks that special tags `@apiNote`, `@implNote`, `@implSpec` have exactly one empty line after
- name: KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS
enabled: true
+# Checks that kdoc does not contain @author tag or date
+- name: KDOC_CONTAINS_DATE_OR_AUTHOR
+ enabled: true
# Checks that KDoc does not contain single line with words 'return', 'get' or 'set'
- name: KDOC_TRIVIAL_KDOC_ON_FUNCTION
enabled: true
@@ -129,9 +132,6 @@
# Checks that header kdoc is located before package directive
- name: HEADER_NOT_BEFORE_PACKAGE
enabled: true
-# Checks that header kdoc does not contain @author tag ar date
-- name: HEADER_CONTAINS_DATE_OR_AUTHOR
- enabled: true
# Checks that file does not contain lines > maxSize
- name: FILE_IS_TOO_LONG
enabled: true
diff --git a/examples/gradle-kotlin-dsl-multiproject/diktat-analysis.yml b/examples/gradle-kotlin-dsl-multiproject/diktat-analysis.yml
index 1dff285a5f..7c503cf769 100644
--- a/examples/gradle-kotlin-dsl-multiproject/diktat-analysis.yml
+++ b/examples/gradle-kotlin-dsl-multiproject/diktat-analysis.yml
@@ -71,6 +71,8 @@
enabled: true
- name: KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS
enabled: true
+- name: KDOC_CONTAINS_DATE_OR_AUTHOR
+ enabled: true
- name: KDOC_TRIVIAL_KDOC_ON_FUNCTION
enabled: 'true'
- name: HEADER_WRONG_FORMAT
@@ -84,8 +86,6 @@
copyrightText: 'Copyright (c) Your Company Name Here. 2010-2021'
- name: HEADER_NOT_BEFORE_PACKAGE
enabled: true
-- name: HEADER_CONTAINS_DATE_OR_AUTHOR
- enabled: true
- name: FILE_IS_TOO_LONG
enabled: true
configuration:
diff --git a/examples/gradle-kotlin-dsl/diktat-analysis.yml b/examples/gradle-kotlin-dsl/diktat-analysis.yml
index cf353c0ce0..e3fcb8b435 100644
--- a/examples/gradle-kotlin-dsl/diktat-analysis.yml
+++ b/examples/gradle-kotlin-dsl/diktat-analysis.yml
@@ -111,6 +111,9 @@
# Checks that special tags `@apiNote`, `@implNote`, `@implSpec` have exactly one empty line after
- name: KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS
enabled: true
+# Checks that kdoc does not contain @author tag or date
+- name: KDOC_CONTAINS_DATE_OR_AUTHOR
+ enabled: true
# Checks that KDoc does not contain single line with words 'return', 'get' or 'set'
- name: KDOC_TRIVIAL_KDOC_ON_FUNCTION
enabled: true
@@ -129,9 +132,6 @@
# Checks that header kdoc is located before package directive
- name: HEADER_NOT_BEFORE_PACKAGE
enabled: true
-# Checks that header kdoc does not contain @author tag ar date
-- name: HEADER_CONTAINS_DATE_OR_AUTHOR
- enabled: true
# Checks that file does not contain lines > maxSize
- name: FILE_IS_TOO_LONG
enabled: true
diff --git a/examples/maven/diktat-analysis.yml b/examples/maven/diktat-analysis.yml
index cf353c0ce0..e3fcb8b435 100644
--- a/examples/maven/diktat-analysis.yml
+++ b/examples/maven/diktat-analysis.yml
@@ -111,6 +111,9 @@
# Checks that special tags `@apiNote`, `@implNote`, `@implSpec` have exactly one empty line after
- name: KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS
enabled: true
+# Checks that kdoc does not contain @author tag or date
+- name: KDOC_CONTAINS_DATE_OR_AUTHOR
+ enabled: true
# Checks that KDoc does not contain single line with words 'return', 'get' or 'set'
- name: KDOC_TRIVIAL_KDOC_ON_FUNCTION
enabled: true
@@ -129,9 +132,6 @@
# Checks that header kdoc is located before package directive
- name: HEADER_NOT_BEFORE_PACKAGE
enabled: true
-# Checks that header kdoc does not contain @author tag ar date
-- name: HEADER_CONTAINS_DATE_OR_AUTHOR
- enabled: true
# Checks that file does not contain lines > maxSize
- name: FILE_IS_TOO_LONG
enabled: true
diff --git a/info/available-rules.md b/info/available-rules.md
index 085763b398..550983f0cb 100644
--- a/info/available-rules.md
+++ b/info/available-rules.md
@@ -1,9 +1,9 @@
| Chap | Standard | Rule name | Description | Fix | Config | FixMe |
| ----- | ---------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | --- | ------ | ----- |
| 1 | 1.1.1 | CONFUSING_IDENTIFIER_NAMING | Check: warns if identifier has inappropriate name (See table of rule 1.2 part 6). | no | no | no |
-| 1 | 1.2.1 | PACKAGE_NAME_MISSING | Check: warns if package name is missing in a file
Fix: automatically adds package directive with the name that starts from the domain name (in example - com.huawei) and contains the real directory | yes | no | Domain name should not be hardcoded. It should be moved to extra configuration.Recursively fix all imports in project.Fix the directory where the code is stored.Make this check isolated from domain name addition|
+| 1 | 1.2.1 | PACKAGE_NAME_MISSING | Check: warns if package name is missing in a file
Fix: automatically adds package directive with the name that starts from the domain name (in example - com.huawei) and contains the real directory | yes | no | Recursively fix all imports in project.
Fix the directory where the code is stored.
Make this check isolated from domain name addition |
| 1 | 1.2.1 | PACKAGE_NAME_INCORRECT_CASE | Check: warns if package name is in incorrect (non-lower) case
Fix: automatically update the case in package name | yes | no | Recursively update all imports in the project.|
-| 1 | 1.2.1 | PACKAGE_NAME_INCORRECT_PREFIX | Check: warns if package name does not start with the company's domain
Fix: automatically update the prefix in the package name | yes | no | Fix the directory where the code is stored.Recursively update all imports in the project.Domain name should not be hardcoded. It should be moved to extra configuration.|
+| 1 | 1.2.1 | PACKAGE_NAME_INCORRECT_PREFIX | Check: warns if package name does not start with the company's domain
Fix: automatically update the prefix in the package name | yes | no | Fix the directory where the code is stored.
Recursively update all imports in the project.|
| 1 | 1.2.1 | PACKAGE_NAME_INCORRECT_SYMBOLS | Check: warns if package name has incorrect symbols like underscore or non-ASCII letters/digits.Exception: underscores that are used for differentiating of keywords in a name.
Fix: no but will be | no | no | Add autofix for at least converting underscore to a dot or replacing itFix the directory where the code is stored.Cover autofix with tests|
| 1 | 1.2.1 | PACKAGE_NAME_INCORRECT_PATH | Check: warns if the path for a file does not match with a package name
Fix: replacing incorrect package name with the name constructed from a path to the file. | yes | no | Make this check isolated from domain name creationRecursively update all imports in the project.Fix the directory where the code is stored.Add test mechanism to test checker|
| 1 | 1.2.1 | INCORRECT_PACKAGE_SEPARATOR | Check: warns if underscore is incorrectly used to split package naming
Fix: fixing all nodes in AST and the package name to remove all underscores | no | no | Recursively update usages of this class in the project|
@@ -38,12 +38,12 @@
| 2 | 2.2.1 | KDOC_NO_EMPTY_TAGS | Check: warns if KDoc tags have empty content | no | no | |
| 2 | 2.1.3 | KDOC_NO_DEPRECATED_TAG | Check: warns if `@deprecated` is used in KDoc
Fix: adds `@Deprecated` annotation with message, removes tag | yes | no | Annotation's `replaceWith` field can be filled too|
| 2 | 2.1.1 | KDOC_NO_CONSTRUCTOR_PROPERTY | Check: warns if there is no property tag inside KDoc before constructor | yes | no |
-| 2 | 2.1.1 | KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT | Check: warns if there is comment before property in constructor | yes | no |
+| 2 | 2.1.1 | KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT| Check: warns if there is comment before property in constructor | yes | no |
+| 2 | 2.2.1 | KDOC_CONTAINS_DATE_OR_AUTHOR | Check: warns if header KDoc contains `@author` tag.
Warns if `@since` tag contains version and not date. | no | no | Detect author by other patterns (e.g. 'created by' etc.)|
| 2 | 2.3.1 | KDOC_TRIVIAL_KDOC_ON_FUNCTION | Check: warns if KDoc contains single line with words 'return', 'get' or 'set' | no | no | |
| 2 | 2.2.1 | HEADER_WRONG_FORMAT | Checks: warns if there is no newline after header KDoc
Fix: adds newline | yes | no | Check if header is on the very top of file. It's hard to determine when it's not.|
| 2 | 2.2.1 | HEADER_MISSING_OR_WRONG_COPYRIGHT | Checks: copyright exists on top of file and is properly formatted (as a block comment)
Fix: adds copyright if it is missing and required | yes | mandatoryCopyright | Allow setting copyright patterns via configuration|
| 2 | 2.2.1 | WRONG_COPYRIGHT_YEAR | Checks: copyright have a valid year
Fix: makes a year valid | yes | no | - |
-| 2 | 2.2.1 | HEADER_CONTAINS_DATE_OR_AUTHOR | Check: warns if header KDoc contains `@author` tag | no | no | Detect author by other patterns (e.g. 'created by' etc.)Detect creation date (no standard tag even in javadoc)|
| 2 | 2.2.1 | HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE | Check: warns if file with zero or >1 classes doesn't have header KDoc | no | no | |
| 2 | 2.4.2 | COMMENTED_OUT_CODE | Check: warns if commented code is detected (when un-commented, can be parsed) | no | no | Offset is lost when joined EOL comments are split again|
| 2 | 2.4.1 | COMMENT_WHITE_SPACE | Check: warns if there is no space between // and comment, if there is no space between code and comment
Fix: adds a white space | yes | maxSpaces | - |
diff --git a/info/guide/guide-chapter-2.md b/info/guide/guide-chapter-2.md
index afe3550248..8a7c5c9d82 100644
--- a/info/guide/guide-chapter-2.md
+++ b/info/guide/guide-chapter-2.md
@@ -94,7 +94,7 @@ When the method has such details as arguments, return value, or can throw except
/**
* This is the short overview comment for the example interface.
* / * Add a blank line between the comment text and each KDoc tag underneath * /
- * @since 2019-01-01
+ * @since 1.6
*/
protected abstract class Sample {
/**
@@ -136,8 +136,11 @@ Therefore, Kdoc should contain the following:
Kdoc should not contain:
- Empty descriptions in tag blocks. It is better not to write Kdoc than waste code line space.
- There should be no empty lines between the method/class declaration and the end of Kdoc (`*/` symbols).
-Important note: KDoc does not support the `@deprecated` tag. Instead, use the `@Deprecated` annotation.
-
+- `@author` tag. It doesn't matter who originally created a class when you can use `git blame` or VCS of your choice to look through the changes history.
+Important notes:
+- KDoc does not support the `@deprecated` tag. Instead, use the `@Deprecated` annotation.
+- The `@since` tag should be used for versions only. Do not use dates in `@since` tag, it's confusing and less accurate.
+
If a tag block cannot be described in one line, indent the content of the new line by *four spaces* from the `@` position to achieve alignment (`@` counts as one + three spaces).
**Exception:**
@@ -172,20 +175,38 @@ Other KDoc tags (such as @param type parameters and @see.) can be added as follo
### 2.2 Adding comments on the file header
This section describes the general rules of adding comments on the file header.
-Comments on the file header should be placed before the package name and imports. If you need to add more content to the comment, subsequently add it in the same format.
-Comments on the file header must include copyright information, without the creation date and author's name (use VCS for history management). Also, describe the content inside files that contain multiple or no classes.
+### 2.2.1 Formatting of comments in the file header
-Place comments on the file header before the package name and imports. If you need to add more content to the comment, subsequently add it in the same format.
+Comments on the file header should be placed before the package name and imports. If you need to add more content to the comment, subsequently add it in the same format.
+
+Comments on the file header must include copyright information, without the creation date and author's name (use VCS for history management).
+Also, describe the content inside files that contain multiple or no classes.
The following examples for Huawei describe the format of the *copyright license*: \
Chinese version: `版权所有 (c) 华为技术有限公司 2012-2020` \
English version: `Copyright (c) Huawei Technologies Co., Ltd. 2012-2020. All rights reserved.`
+`2012` and `2020` are the years the file was first created and the current year, respectively.
+
+Do not place **release notes** in header, use VCS to keep track of changes in file. Notable changes can be marked in individual KDocs using `@since` tag with version.
+
+Invalid example:
+```kotlin
+/**
+ * Release notes:
+ * 2019-10-11: added class Foo
+ */
-Regarding the **release notes**, see examples below:
+class Foo
+```
-- `2012-2020` can be modified according to your actual situation. `2012` and `2020` are the years the file was first created and last modified, respectively.
-These two years can be the same (for example, `2020–2020`). When the file is substantially changed (for example, through feature extensions and major refactorings), the subsequent years must be updated.
+Valid example:
+```kotlin
+/**
+ * @since 2.4.0
+ */
+class Foo
+```
- The **copyright statement** can use your company's subsidiaries, as shown in the below examples: \
Chinese version: `版权所有 (c) 海思半导体 2012-2020` \
From 8344d7067e9d21c1ffce4616cc409c328d7b6b5a Mon Sep 17 00:00:00 2001
From: Alexander Tsay <48321920+aktsay6@users.noreply.github.com>
Date: Thu, 14 Jan 2021 14:14:46 +0300
Subject: [PATCH 05/11] Bugfix. Problems with year checking logic in
copyright(#688) (#693)
* bugfix/new-year-npe(#688)
### What's done:
* Added new warning
* Updated Tests
* Fixed bugs
---
.../diktat/ruleset/generation/Generation.kt | 41 ++++++++++---------
.../rules/comments/HeaderCommentRule.kt | 17 ++++++--
.../chapter2/HeaderCommentRuleFixTest.kt | 11 +++++
.../header/CopyrightDifferentYearExpected.kt | 4 +-
.../header/CopyrightDifferentYearTest.kt | 4 +-
.../CopyrightShouldNotTriggerNPEExpected.kt | 19 +++++++++
.../CopyrightShouldNotTriggerNPETest.kt | 16 ++++++++
.../header/MisplacedHeaderKdocExpected.kt | 2 -
.../header/MisplacedHeaderKdocTest.kt | 2 +-
.../header/MultilineCopyrightExample.kt | 2 +-
.../MultilineCopyrightNotTriggerExample.kt | 4 +-
.../MultilineCopyrightNotTriggerTest.kt | 4 +-
.../header/MultilineCopyrightTest.kt | 2 +-
.../header/NewlineAfterHeaderKdocExpected.kt | 1 -
.../header/NewlineAfterHeaderKdocTest.kt | 2 +-
15 files changed, 94 insertions(+), 37 deletions(-)
create mode 100644 diktat-rules/src/test/resources/test/paragraph2/header/CopyrightShouldNotTriggerNPEExpected.kt
create mode 100644 diktat-rules/src/test/resources/test/paragraph2/header/CopyrightShouldNotTriggerNPETest.kt
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/generation/Generation.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/generation/Generation.kt
index 93cd94ddbb..946de2a73a 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/generation/Generation.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/generation/Generation.kt
@@ -57,25 +57,28 @@ private fun generateWarningNames() {
}
private fun validateYear() {
- val file = File("diktat-rules/src/test/resources/test/paragraph2/header/CopyrightDifferentYearExpected.kt")
- val tempFile = createTempFile()
- tempFile.printWriter().use { writer ->
- file.forEachLine { line ->
- writer.println(when {
- hyphenRegex.matches(line) -> hyphenRegex.replace(line) {
- val years = it.value.split("-")
- val validYears = "${years[0]}-$curYear"
- line.replace(hyphenRegex, validYears)
+ val files = File("diktat-rules/src/test/resources/test/paragraph2/header")
+ files
+ .listFiles()
+ .filterNot { it.name.contains("CopyrightDifferentYearTest.kt") }
+ .forEach { file ->
+ val tempFile = createTempFile()
+ tempFile.printWriter().use { writer ->
+ file.forEachLine { line ->
+ writer.println(when {
+ line.contains(hyphenRegex) -> line.replace(hyphenRegex) {
+ val years = it.value.split("-")
+ "${years[0]}-$curYear"
+ }
+ line.contains(afterCopyrightRegex) -> line.replace(afterCopyrightRegex) {
+ val copyrightYears = it.value.split("(c)", "(C)", "©")
+ "${copyrightYears[0]}-$curYear"
+ }
+ else -> line
+ })
}
- afterCopyrightRegex.matches(line) -> afterCopyrightRegex.replace(line) {
- val copyrightYears = it.value.split("(c)", "(C)", "©")
- val validYears = "${copyrightYears[0]}-$curYear"
- line.replace(afterCopyrightRegex, validYears)
- }
- else -> line
- })
+ }
+ file.delete()
+ tempFile.renameTo(file)
}
- }
- file.delete()
- tempFile.renameTo(file)
}
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 ffe278ba89..2f799d34a3 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
@@ -16,6 +16,7 @@ import org.cqfn.diktat.ruleset.utils.findChildBefore
import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType
import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
import org.cqfn.diktat.ruleset.utils.kDocTags
+import org.cqfn.diktat.ruleset.utils.log
import org.cqfn.diktat.ruleset.utils.moveChildBefore
import com.pinterest.ktlint.core.Rule
@@ -142,15 +143,24 @@ class HeaderCommentRule(private val configRules: List) : Rule("head
return
}
+ if (makeCopyrightCorrectYear(configuration.getCopyrightText()).isNotEmpty()) {
+ log.warn("Copyright year is not up do date.")
+ }
+
+ // need to make sure that copyright year is consistent with current year
val copyrightText = configuration.getCopyrightText()
val headerComment = node.findChildBefore(PACKAGE_DIRECTIVE, BLOCK_COMMENT)
- val isWrongCopyright = headerComment != null && !headerComment.text.flatten().contains(copyrightText.flatten())
+ // Depends only on content and doesn't consider years
+ val isWrongCopyright = headerComment != null &&
+ !headerComment.text.flatten().contains(copyrightText.flatten()) &&
+ !headerComment.text.flatten().contains(makeCopyrightCorrectYear(copyrightText).flatten())
val isMissingCopyright = headerComment == null && configuration.isCopyrightMandatory()
val isCopyrightInsideKdoc = (node.getAllChildrenWithType(KDOC) + node.getAllChildrenWithType(ElementType.EOL_COMMENT))
.any { commentNode ->
copyrightWords.any { commentNode.text.contains(it, ignoreCase = true) }
}
+
if (isWrongCopyright || isMissingCopyright || isCopyrightInsideKdoc) {
val freeText = when {
// If `isCopyrightInsideKdoc` then `isMissingCopyright` is true too, but warning text from `isCopyrightInsideKdoc` is preferable.
@@ -177,7 +187,8 @@ class HeaderCommentRule(private val configRules: List) : Rule("head
val copyrightWithCorrectYear = makeCopyrightCorrectYear(copyrightText)
- if (copyrightWithCorrectYear.isNotEmpty()) {
+ // Triggers when there is a copyright, but its year is not updated.
+ if (!isMissingCopyright && copyrightWithCorrectYear.isNotEmpty()) {
WRONG_COPYRIGHT_YEAR.warnAndFix(configRules, emitWarn, isFixMode, "year should be $curYear", node.startOffset, node) {
(headerComment as LeafElement).replaceWithText(headerComment.text.replace(copyrightText, copyrightWithCorrectYear))
}
@@ -233,7 +244,7 @@ class HeaderCommentRule(private val configRules: List) : Rule("head
}
companion object {
- val hyphenRegex = Regex("""\b(\d+-\d+)\b""")
+ val hyphenRegex = Regex("""\d+-\d+""")
val afterCopyrightRegex = Regex("""((©|\([cC]\))+ *\d+)""")
val curYear = LocalDate.now().year
}
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleFixTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleFixTest.kt
index 65628ffe74..d33e1c7440 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleFixTest.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleFixTest.kt
@@ -71,6 +71,17 @@ class HeaderCommentRuleFixTest : FixTestBase(
)
}
+ @Test
+ @Tag(WRONG_COPYRIGHT_YEAR)
+ fun `should not raise npe`() {
+ fixAndCompare("CopyrightShouldNotTriggerNPEExpected.kt", "CopyrightShouldNotTriggerNPETest.kt",
+ listOf(RulesConfig(HEADER_MISSING_OR_WRONG_COPYRIGHT.name, true, mapOf(
+ "isCopyrightMandatory" to "true",
+ "copyrightText" to "Copyright (c) My Company., Ltd. 2012-2021. All rights reserved."
+ )))
+ )
+ }
+
@Test
@Tag(WarningNames.HEADER_MISSING_OR_WRONG_COPYRIGHT)
fun `copyright multiline`() {
diff --git a/diktat-rules/src/test/resources/test/paragraph2/header/CopyrightDifferentYearExpected.kt b/diktat-rules/src/test/resources/test/paragraph2/header/CopyrightDifferentYearExpected.kt
index a344438ff6..fe33eb4e52 100644
--- a/diktat-rules/src/test/resources/test/paragraph2/header/CopyrightDifferentYearExpected.kt
+++ b/diktat-rules/src/test/resources/test/paragraph2/header/CopyrightDifferentYearExpected.kt
@@ -1,6 +1,6 @@
/*
- * Copyright (c) My Company., Ltd. 2012-2021. All rights reserved.
- */
+ Copyright (c) My Company., Ltd. 2012-2021. All rights reserved.
+*/
/**
* Lorem ipsum
* dolor sit amet
diff --git a/diktat-rules/src/test/resources/test/paragraph2/header/CopyrightDifferentYearTest.kt b/diktat-rules/src/test/resources/test/paragraph2/header/CopyrightDifferentYearTest.kt
index fb10f5bcf9..89caa66aa6 100644
--- a/diktat-rules/src/test/resources/test/paragraph2/header/CopyrightDifferentYearTest.kt
+++ b/diktat-rules/src/test/resources/test/paragraph2/header/CopyrightDifferentYearTest.kt
@@ -1,6 +1,6 @@
/*
- * Copyright (c) My Company., Ltd. 2012-2019. All rights reserved.
- */
+ Copyright (c) My Company., Ltd. 2012-2019. All rights reserved.
+*/
/**
* Lorem ipsum
* dolor sit amet
diff --git a/diktat-rules/src/test/resources/test/paragraph2/header/CopyrightShouldNotTriggerNPEExpected.kt b/diktat-rules/src/test/resources/test/paragraph2/header/CopyrightShouldNotTriggerNPEExpected.kt
new file mode 100644
index 0000000000..fe33eb4e52
--- /dev/null
+++ b/diktat-rules/src/test/resources/test/paragraph2/header/CopyrightShouldNotTriggerNPEExpected.kt
@@ -0,0 +1,19 @@
+/*
+ Copyright (c) My Company., Ltd. 2012-2021. All rights reserved.
+*/
+/**
+ * Lorem ipsum
+ * dolor sit amet
+ */
+
+package test.paragraph2.header
+
+import org.cqfn.diktat.example.A
+import org.cqfn.diktat.example.B
+
+/**
+ * Example class
+ */
+class Example {
+ lateinit var map: Map
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph2/header/CopyrightShouldNotTriggerNPETest.kt b/diktat-rules/src/test/resources/test/paragraph2/header/CopyrightShouldNotTriggerNPETest.kt
new file mode 100644
index 0000000000..a0e91a476b
--- /dev/null
+++ b/diktat-rules/src/test/resources/test/paragraph2/header/CopyrightShouldNotTriggerNPETest.kt
@@ -0,0 +1,16 @@
+/**
+ * Lorem ipsum
+ * dolor sit amet
+ */
+
+package test.paragraph2.header
+
+import org.cqfn.diktat.example.A
+import org.cqfn.diktat.example.B
+
+/**
+ * Example class
+ */
+class Example {
+ lateinit var map: Map
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph2/header/MisplacedHeaderKdocExpected.kt b/diktat-rules/src/test/resources/test/paragraph2/header/MisplacedHeaderKdocExpected.kt
index 44ca966073..aaed183d0e 100644
--- a/diktat-rules/src/test/resources/test/paragraph2/header/MisplacedHeaderKdocExpected.kt
+++ b/diktat-rules/src/test/resources/test/paragraph2/header/MisplacedHeaderKdocExpected.kt
@@ -1,8 +1,6 @@
/*
Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved.
*/
-
-
/**
* Lorem ipsum
* dolor sit amet
diff --git a/diktat-rules/src/test/resources/test/paragraph2/header/MisplacedHeaderKdocTest.kt b/diktat-rules/src/test/resources/test/paragraph2/header/MisplacedHeaderKdocTest.kt
index c9437d5cc1..2417795db7 100644
--- a/diktat-rules/src/test/resources/test/paragraph2/header/MisplacedHeaderKdocTest.kt
+++ b/diktat-rules/src/test/resources/test/paragraph2/header/MisplacedHeaderKdocTest.kt
@@ -1,5 +1,5 @@
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved.
*/
package test.paragraph2.header
diff --git a/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightExample.kt b/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightExample.kt
index db771bc4f3..0370a2fd65 100644
--- a/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightExample.kt
+++ b/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightExample.kt
@@ -18,4 +18,4 @@ class SomeClass {
fun coolFun() {
val a = 5
}
-}
\ No newline at end of file
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightNotTriggerExample.kt b/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightNotTriggerExample.kt
index 3b9b0af741..93186bf742 100644
--- a/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightNotTriggerExample.kt
+++ b/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightNotTriggerExample.kt
@@ -1,5 +1,5 @@
/*
- Copyright 2018-2020 John Doe.
+ Copyright 2018-2021 John Doe.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -12,4 +12,4 @@ class SomeClass {
fun function() {
}
-}
\ No newline at end of file
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightNotTriggerTest.kt b/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightNotTriggerTest.kt
index 3b9b0af741..93186bf742 100644
--- a/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightNotTriggerTest.kt
+++ b/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightNotTriggerTest.kt
@@ -1,5 +1,5 @@
/*
- Copyright 2018-2020 John Doe.
+ Copyright 2018-2021 John Doe.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -12,4 +12,4 @@ class SomeClass {
fun function() {
}
-}
\ No newline at end of file
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightTest.kt b/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightTest.kt
index 481563563b..f4a1441370 100644
--- a/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightTest.kt
+++ b/diktat-rules/src/test/resources/test/paragraph2/header/MultilineCopyrightTest.kt
@@ -2,4 +2,4 @@ class SomeClass {
fun coolFun() {
val a = 5
}
-}
\ No newline at end of file
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph2/header/NewlineAfterHeaderKdocExpected.kt b/diktat-rules/src/test/resources/test/paragraph2/header/NewlineAfterHeaderKdocExpected.kt
index 17d54923d7..84cc556f19 100644
--- a/diktat-rules/src/test/resources/test/paragraph2/header/NewlineAfterHeaderKdocExpected.kt
+++ b/diktat-rules/src/test/resources/test/paragraph2/header/NewlineAfterHeaderKdocExpected.kt
@@ -1,7 +1,6 @@
/*
Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved.
*/
-
/**
* This is a file used in unit test
*/
diff --git a/diktat-rules/src/test/resources/test/paragraph2/header/NewlineAfterHeaderKdocTest.kt b/diktat-rules/src/test/resources/test/paragraph2/header/NewlineAfterHeaderKdocTest.kt
index 455c530d4c..12b0028ebf 100644
--- a/diktat-rules/src/test/resources/test/paragraph2/header/NewlineAfterHeaderKdocTest.kt
+++ b/diktat-rules/src/test/resources/test/paragraph2/header/NewlineAfterHeaderKdocTest.kt
@@ -1,5 +1,5 @@
/*
- Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
+ Copyright (c) Huawei Technologies Co., Ltd. 2020-2021. All rights reserved.
*/
/**
* This is a file used in unit test
From 241f45e578d5afefa16a981075b08b2f486d90d9 Mon Sep 17 00:00:00 2001
From: Alexander Tsay <48321920+aktsay6@users.noreply.github.com>
Date: Thu, 14 Jan 2021 14:40:20 +0300
Subject: [PATCH 06/11] =?UTF-8?q?Do=20not=20use=20!isEmpty=20methods=20fro?=
=?UTF-8?q?m=20the=20collections=20=E2=80=93=20need=20to=20use=20isNotEmpt?=
=?UTF-8?q?y=20instead=20(#704)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feature/check-isEmpty-method(#692)
### What's done:
* Added new rule
* Added warn tests
* Added fix tests
---
diktat-analysis.yml | 3 +
.../src/main/kotlin/generated/WarningNames.kt | 2 +
.../cqfn/diktat/ruleset/constants/Warnings.kt | 1 +
.../ruleset/rules/CheckInverseMethodRule.kt | 73 ++++++++++++++++++
.../ruleset/rules/DiktatRuleSetProvider.kt | 1 +
.../main/resources/diktat-analysis-huawei.yml | 3 +
.../src/main/resources/diktat-analysis.yml | 3 +
.../chapter5/CheckInverseMethodRuleFixTest.kt | 16 ++++
.../CheckInverseMethodRuleWarnTest.kt | 74 +++++++++++++++++++
.../ReplaceMethodCallNamesExpected.kt | 25 +++++++
.../ReplaceMethodCallNamesTest.kt | 25 +++++++
info/available-rules.md | 1 +
info/guide/diktat-coding-convention.md | 24 ++++++
info/guide/guide-TOC.md | 3 +-
info/guide/guide-chapter-5.md | 24 ++++++
15 files changed, 277 insertions(+), 1 deletion(-)
create mode 100644 diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/CheckInverseMethodRule.kt
create mode 100644 diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter5/CheckInverseMethodRuleFixTest.kt
create mode 100644 diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter5/CheckInverseMethodRuleWarnTest.kt
create mode 100644 diktat-rules/src/test/resources/test/paragraph5/method_call_names/ReplaceMethodCallNamesExpected.kt
create mode 100644 diktat-rules/src/test/resources/test/paragraph5/method_call_names/ReplaceMethodCallNamesTest.kt
diff --git a/diktat-analysis.yml b/diktat-analysis.yml
index c139b4c94b..9ffbf8021f 100644
--- a/diktat-analysis.yml
+++ b/diktat-analysis.yml
@@ -413,4 +413,7 @@
enabled: true
# If there is stateless class it is preferred to use object
- name: OBJECT_IS_PREFERRED
+ enabled: true
+# If there exists negated version of function you should prefer it instead of !functionCall
+- name: INVERSE_FUNCTION_PREFERRED
enabled: true
\ No newline at end of file
diff --git a/diktat-rules/src/main/kotlin/generated/WarningNames.kt b/diktat-rules/src/main/kotlin/generated/WarningNames.kt
index 826f16ba87..61bc1fad94 100644
--- a/diktat-rules/src/main/kotlin/generated/WarningNames.kt
+++ b/diktat-rules/src/main/kotlin/generated/WarningNames.kt
@@ -229,4 +229,6 @@ public object WarningNames {
public const val AVOID_USING_UTILITY_CLASS: String = "AVOID_USING_UTILITY_CLASS"
public const val OBJECT_IS_PREFERRED: String = "OBJECT_IS_PREFERRED"
+
+ public const val INVERSE_FUNCTION_PREFERRED: String = "INVERSE_FUNCTION_PREFERRED"
}
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 0d43046e44..981b21b565 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
@@ -147,6 +147,7 @@ enum class Warnings(
NO_CORRESPONDING_PROPERTY(false, "6.1.7", "backing property should have the same name, but there is no corresponding property"),
AVOID_USING_UTILITY_CLASS(false, "6.4.1", "avoid using utility classes/objects, use extensions functions"),
OBJECT_IS_PREFERRED(true, "6.4.2", "it is better to use object for stateless classes"),
+ INVERSE_FUNCTION_PREFERRED(true, "5.1.4", "it is better to use inverse function"),
;
/**
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/CheckInverseMethodRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/CheckInverseMethodRule.kt
new file mode 100644
index 0000000000..2f11475454
--- /dev/null
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/CheckInverseMethodRule.kt
@@ -0,0 +1,73 @@
+package org.cqfn.diktat.ruleset.rules
+
+import org.cqfn.diktat.common.config.rules.RulesConfig
+import org.cqfn.diktat.ruleset.constants.EmitType
+import org.cqfn.diktat.ruleset.constants.Warnings.INVERSE_FUNCTION_PREFERRED
+
+import com.pinterest.ktlint.core.Rule
+import com.pinterest.ktlint.core.ast.ElementType.BLOCK_COMMENT
+import com.pinterest.ktlint.core.ast.ElementType.CALL_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.IDENTIFIER
+import com.pinterest.ktlint.core.ast.ElementType.LPAR
+import com.pinterest.ktlint.core.ast.ElementType.OPERATION_REFERENCE
+import com.pinterest.ktlint.core.ast.ElementType.REFERENCE_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.RPAR
+import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST
+import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
+import org.jetbrains.kotlin.com.intellij.lang.ASTNode
+import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement
+import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
+import org.jetbrains.kotlin.psi.psiUtil.siblings
+
+/**
+ * This rule checks if inverse method can be used.
+ * For example if there is !isEmpty() on collection call that it changes it to isNotEmpty()
+ */
+class CheckInverseMethodRule(private val configRules: List) : Rule("inverse-method") {
+ private var isFixMode: Boolean = false
+ private lateinit var emitWarn: EmitType
+
+ override fun visit(node: ASTNode,
+ autoCorrect: Boolean,
+ emit: EmitType) {
+ emitWarn = emit
+ isFixMode = autoCorrect
+
+ if (node.elementType == CALL_EXPRESSION && node.text in methodMap.keys) {
+ checkCallExpressionName(node)
+ }
+ }
+
+ private fun checkCallExpressionName(node: ASTNode) {
+ val operationRef = node
+ .treeParent
+ .siblings(forward = false)
+ .takeWhile { it.elementType in intermediateTokens }
+ .firstOrNull { it.elementType == OPERATION_REFERENCE}
+ if (operationRef?.text == "!") {
+ INVERSE_FUNCTION_PREFERRED.warnAndFix(configRules, emitWarn, isFixMode, "${methodMap[node.text]} instead of !${node.text}", node.startOffset, node) {
+ val callExpression = CompositeElement(CALL_EXPRESSION)
+ val referenceExp = CompositeElement(REFERENCE_EXPRESSION)
+ val argList = CompositeElement(VALUE_ARGUMENT_LIST)
+ node.treeParent.addChild(callExpression, node)
+ callExpression.addChild(referenceExp)
+ callExpression.addChild(argList)
+ referenceExp.addChild(LeafPsiElement(IDENTIFIER, "${methodMap[node.text]}".dropLast(2)))
+ argList.addChild(LeafPsiElement(LPAR, "("))
+ argList.addChild(LeafPsiElement(RPAR, ")"))
+ node.treeParent.treeParent.removeChild(node.treeParent.treePrev) // removing OPERATION_EXPRESSION - !
+ node.treeParent.removeChild(node)
+ }
+ }
+ }
+
+ companion object {
+ val methodMap = mapOf(
+ "isEmpty()" to "isNotEmpty()",
+ "isBlank()" to "isNotBlank()",
+ "isNotEmpty()" to "isEmpty()",
+ "isNotBlank()" to "isBlank()"
+ )
+ val intermediateTokens = listOf(WHITE_SPACE, OPERATION_REFERENCE, BLOCK_COMMENT)
+ }
+}
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt
index 5fc4b700b2..7640a6f678 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/DiktatRuleSetProvider.kt
@@ -111,6 +111,7 @@ class DiktatRuleSetProvider(private var diktatConfigFile: String = DIKTAT_ANALYS
::CustomGetterSetterRule,
::CompactInitialization,
// other rules
+ ::CheckInverseMethodRule,
::StatelessClassesRule,
::ImplicitBackingPropertyRule,
::StringTemplateFormatRule,
diff --git a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml
index dc4b98a7df..3e7f60fdb6 100644
--- a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml
+++ b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml
@@ -413,4 +413,7 @@
enabled: true
# If there is stateless class it is preferred to use object
- name: OBJECT_IS_PREFERRED
+ enabled: true
+# If there exists negated version of function you should prefer it instead of !functionCall
+- name: INVERSE_FUNCTION_PREFERRED
enabled: true
\ No newline at end of file
diff --git a/diktat-rules/src/main/resources/diktat-analysis.yml b/diktat-rules/src/main/resources/diktat-analysis.yml
index 0c9e986f18..0caa29ff1e 100644
--- a/diktat-rules/src/main/resources/diktat-analysis.yml
+++ b/diktat-rules/src/main/resources/diktat-analysis.yml
@@ -410,4 +410,7 @@
enabled: true
# If there is stateless class it is preferred to use object
- name: OBJECT_IS_PREFERRED
+ enabled: true
+# If there exists negated version of function you should prefer it instead of !functionCall
+- name: INVERSE_FUNCTION_PREFERRED
enabled: true
\ No newline at end of file
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter5/CheckInverseMethodRuleFixTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter5/CheckInverseMethodRuleFixTest.kt
new file mode 100644
index 0000000000..58d7f86c7b
--- /dev/null
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter5/CheckInverseMethodRuleFixTest.kt
@@ -0,0 +1,16 @@
+package org.cqfn.diktat.ruleset.chapter5
+
+import org.cqfn.diktat.ruleset.rules.CheckInverseMethodRule
+import org.cqfn.diktat.util.FixTestBase
+
+import generated.WarningNames.INVERSE_FUNCTION_PREFERRED
+import org.junit.jupiter.api.Tag
+import org.junit.jupiter.api.Test
+
+class CheckInverseMethodRuleFixTest : FixTestBase("test/paragraph5/method_call_names", ::CheckInverseMethodRule) {
+ @Test
+ @Tag(INVERSE_FUNCTION_PREFERRED)
+ fun `should fix method calls`() {
+ fixAndCompare("ReplaceMethodCallNamesExpected.kt", "ReplaceMethodCallNamesTest.kt")
+ }
+}
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter5/CheckInverseMethodRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter5/CheckInverseMethodRuleWarnTest.kt
new file mode 100644
index 0000000000..924fd8f009
--- /dev/null
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter5/CheckInverseMethodRuleWarnTest.kt
@@ -0,0 +1,74 @@
+package org.cqfn.diktat.ruleset.chapter5
+
+import org.cqfn.diktat.ruleset.constants.Warnings
+import org.cqfn.diktat.ruleset.rules.CheckInverseMethodRule
+import org.cqfn.diktat.ruleset.rules.DIKTAT_RULE_SET_ID
+import org.cqfn.diktat.util.LintTestBase
+
+import com.pinterest.ktlint.core.LintError
+import generated.WarningNames.INVERSE_FUNCTION_PREFERRED
+import org.junit.jupiter.api.Tag
+import org.junit.jupiter.api.Test
+
+class CheckInverseMethodRuleWarnTest : LintTestBase(::CheckInverseMethodRule) {
+ private val ruleId = "$DIKTAT_RULE_SET_ID:inverse-method"
+
+ @Test
+ @Tag(INVERSE_FUNCTION_PREFERRED)
+ fun `should not raise warning`() {
+ lintMethod(
+ """
+ |fun some() {
+ | if (list.isEmpty()) {
+ | // some cool logic
+ | }
+ |}
+ """.trimMargin()
+ )
+ }
+
+ @Test
+ @Tag(INVERSE_FUNCTION_PREFERRED)
+ fun `should raise warning`() {
+ lintMethod(
+ """
+ |fun some() {
+ | if (!list.isEmpty()) {
+ | // some cool logic
+ | }
+ |}
+ """.trimMargin(),
+ LintError(2, 14, ruleId, "${Warnings.INVERSE_FUNCTION_PREFERRED.warnText()} isNotEmpty() instead of !isEmpty()", true)
+ )
+ }
+
+ @Test
+ @Tag(INVERSE_FUNCTION_PREFERRED)
+ fun `should consider white spaces between ! and call expression`() {
+ lintMethod(
+ """
+ |fun some() {
+ | if (! list.isEmpty()) {
+ | // some cool logic
+ | }
+ |}
+ """.trimMargin(),
+ LintError(2, 16, ruleId, "${Warnings.INVERSE_FUNCTION_PREFERRED.warnText()} isNotEmpty() instead of !isEmpty()", true)
+ )
+ }
+
+ @Test
+ @Tag(INVERSE_FUNCTION_PREFERRED)
+ fun `should consider comments between ! and call expression`() {
+ lintMethod(
+ """
+ |fun some() {
+ | if (! /*cool comment*/ list.isEmpty()) {
+ | // some cool logic
+ | }
+ |}
+ """.trimMargin(),
+ LintError(2, 32, ruleId, "${Warnings.INVERSE_FUNCTION_PREFERRED.warnText()} isNotEmpty() instead of !isEmpty()", true)
+ )
+ }
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph5/method_call_names/ReplaceMethodCallNamesExpected.kt b/diktat-rules/src/test/resources/test/paragraph5/method_call_names/ReplaceMethodCallNamesExpected.kt
new file mode 100644
index 0000000000..00606b0492
--- /dev/null
+++ b/diktat-rules/src/test/resources/test/paragraph5/method_call_names/ReplaceMethodCallNamesExpected.kt
@@ -0,0 +1,25 @@
+package test.chapter6.method_call_names
+
+fun coolFunction() {
+ val list = listOf(1, 2, 3)
+ val testStr = ""
+
+ if (list.isEmpty()) { }
+
+ if (list.isNotEmpty()) { }
+
+ if (list.isNotEmpty()) { }
+
+ if (list.isEmpty()) { }
+
+ if (testStr.isBlank()) { }
+
+ if (testStr.isNotBlank()) { }
+
+ if (testStr.isNotBlank()) { }
+
+ if (testStr.isBlank()) { }
+
+ if (list.isNotEmpty() && testStr.isBlank()) { }
+}
+
diff --git a/diktat-rules/src/test/resources/test/paragraph5/method_call_names/ReplaceMethodCallNamesTest.kt b/diktat-rules/src/test/resources/test/paragraph5/method_call_names/ReplaceMethodCallNamesTest.kt
new file mode 100644
index 0000000000..49b999d2a0
--- /dev/null
+++ b/diktat-rules/src/test/resources/test/paragraph5/method_call_names/ReplaceMethodCallNamesTest.kt
@@ -0,0 +1,25 @@
+package test.chapter6.method_call_names
+
+fun coolFunction() {
+ val list = listOf(1, 2, 3)
+ val testStr = ""
+
+ if (list.isEmpty()) { }
+
+ if (list.isNotEmpty()) { }
+
+ if (!list.isEmpty()) { }
+
+ if (!list.isNotEmpty()) { }
+
+ if (testStr.isBlank()) { }
+
+ if (testStr.isNotBlank()) { }
+
+ if (!testStr.isBlank()) { }
+
+ if (!testStr.isNotBlank()) { }
+
+ if (!list.isEmpty() && !testStr.isNotBlank()) { }
+}
+
diff --git a/info/available-rules.md b/info/available-rules.md
index 550983f0cb..6af0e09a05 100644
--- a/info/available-rules.md
+++ b/info/available-rules.md
@@ -91,6 +91,7 @@
| 5 | 5.1.1 | TOO_LONG_FUNCTION | Check: if length of function is too long | no | maxFunctionLength isIncludeHeader |
| 5 | 5.1.2 | NESTED_BLOCK | Check if function has more nested blocks than expected | no | maxNestedBlockQuantit|
| 5 | 5.1.3 | AVOID_NESTED_FUNCTIONS | Check: if there are any nested functions
Fix: declare function in the outer scope | yes | no | + |
+| 5 | 5.1.4 | INVERSE_FUNCTION_PREFERRED | Check: if function call with "!" can be rewritten to the inverse function (!isEmpty() -> isNotEmpty())
Fix: Rewrites function call | yes | - | - |
| 5 | 5.2.1 | LAMBDA_IS_NOT_LAST_PARAMETER | Checks that lambda inside function parameters isn't in the end | no | no |
| 5 | 5.2.2 | TOO_MANY_PARAMETERS | Check: if function contains more parameters than allowed | no | maxParameterListSize |
| 5 | 5.2.3 | WRONG_OVERLOADING_FUNCTION_ARGUMENTS | Check: function has overloading instead use default arguments | no | no |
diff --git a/info/guide/diktat-coding-convention.md b/info/guide/diktat-coding-convention.md
index 0450dc2531..c58621e6e5 100644
--- a/info/guide/diktat-coding-convention.md
+++ b/info/guide/diktat-coding-convention.md
@@ -1885,6 +1885,30 @@ fun foo() {
println("Nested Output: ${nested()}")
}
```
+#### 5.1.4 Negated function calls
+Don't use negated function calls if it can be replaced with negated version of this function
+
+**Invalid example**:
+```kotlin
+fun foo() {
+ val list = listOf(1, 2, 3)
+
+ if (!list.isEmpty()) {
+ // Some cool logic
+ }
+}
+```
+
+**Valid example**:
+```kotlin
+fun foo() {
+ val list = listOf(1, 2, 3)
+
+ if (list.isNotEmpty()) {
+ // Some cool logic
+ }
+}
+```
### 5.2 Function arguments
diff --git a/info/guide/guide-TOC.md b/info/guide/guide-TOC.md
index 524e30dad2..5618f99bb2 100644
--- a/info/guide/guide-TOC.md
+++ b/info/guide/guide-TOC.md
@@ -82,7 +82,8 @@ I [Preface](#c0)
* [5.1 Function design](#c5.1)
* [5.1.1 Avoid functions that are too long ](#r5.1.1)
* [5.1.2 Avoid deep nesting of function code blocks, limiting to four levels](#r5.1.2)
- * [5.1.3 Avoid using nested functions](#r5.1.3)
+ * [5.1.3 Avoid using nested functions](#r5.1.3)
+ * [5.1.4 Negated function calls](#r5.1.4)
* [5.2 Function arguments](#c5.2)
* [5.2.1 The lambda parameter of the function should be placed at the end of the argument list](#r5.2.1)
* [5.2.2 Number of function parameters should be limited to five](#r5.2.2)
diff --git a/info/guide/guide-chapter-5.md b/info/guide/guide-chapter-5.md
index d9623e54f2..294418dce4 100644
--- a/info/guide/guide-chapter-5.md
+++ b/info/guide/guide-chapter-5.md
@@ -59,6 +59,30 @@ fun foo() {
println("Nested Output: ${nested()}")
}
```
+#### 5.1.4 Negated function calls
+Don't use negated function calls if it can be replaced with negated version of this function
+
+**Invalid example**:
+```kotlin
+fun foo() {
+ val list = listOf(1, 2, 3)
+
+ if (!list.isEmpty()) {
+ // Some cool logic
+ }
+}
+```
+
+**Valid example**:
+```kotlin
+fun foo() {
+ val list = listOf(1, 2, 3)
+
+ if (list.isNotEmpty()) {
+ // Some cool logic
+ }
+}
+```
### 5.2 Function arguments
From d34ed1c7eafbc8c6b1d3551569dcc0fc5a7737fd Mon Sep 17 00:00:00 2001
From: Denis Kumar
Date: Thu, 14 Jan 2021 17:56:55 +0300
Subject: [PATCH 07/11] Final version of white-paper (#655)
* Final version of white-paper
### What's done:
Fixed final bugs in wp
---
README.md | 6 +-
.../docs/GenerationAvailableRules.kt | 2 +-
.../diktat/generation/docs/GenerationDocs.kt | 105 +++--
info/guide/diktat-coding-convention.md | 46 +-
info/guide/guide-TOC.md | 2 +-
info/guide/guide-chapter-2.md | 2 +-
info/rules-mapping.md | 3 +-
wp/README.md | 9 +
wp/makefile | 2 +
wp/references.bib | 20 +-
wp/sections/appendix.tex | 427 ++++++++++--------
wp/sections/compare.tex | 100 ++--
wp/sections/conclusion.tex | 4 +-
wp/sections/definition.tex | 13 +-
wp/sections/diKTat.tex | 10 +-
wp/sections/download.tex | 93 +++-
wp/sections/examples.tex | 1 -
wp/sections/feature.tex | 81 ++--
wp/sections/introduction.tex | 8 +-
wp/sections/kotlin.tex | 44 +-
wp/sections/work.tex | 157 +++++--
wp/wp.pdf | Bin 0 -> 1587863 bytes
wp/wp.tex | 46 +-
23 files changed, 749 insertions(+), 432 deletions(-)
create mode 100644 wp/README.md
create mode 100644 wp/makefile
delete mode 100644 wp/sections/examples.tex
create mode 100644 wp/wp.pdf
diff --git a/README.md b/README.md
index 46cfa6a1c7..0f3a8f5295 100644
--- a/README.md
+++ b/README.md
@@ -26,9 +26,9 @@ Now diKTat was already added to the lists of [static analysis tools](https://git
## See first
-| | | | |
-| --- | --- | --- | --- |
-|[DiKTat codestyle](info/guide/diktat-coding-convention.md)|[Supported Rules](info/available-rules.md) | [Examples of Usage](https://github.com/akuleshov7/diktat-examples) | [Online Demo](https://ktlint-demo.herokuapp.com) | [Whitepaper](wp/wp.pdf) |
+| | | | | |
+| --- | --- | --- | --- | --- |
+|[DiKTat codestyle](info/guide/diktat-coding-convention.md)|[Supported Rules](info/available-rules.md) | [Examples of Usage](https://github.com/akuleshov7/diktat-examples) | [Online Demo](https://ktlint-demo.herokuapp.com) | [White Paper](wp/wp.pdf) |
## Why should I use diktat in my CI/CD?
diff --git a/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/GenerationAvailableRules.kt b/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/GenerationAvailableRules.kt
index 9585927619..d1757c9c53 100644
--- a/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/GenerationAvailableRules.kt
+++ b/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/GenerationAvailableRules.kt
@@ -18,7 +18,7 @@ fun generateAvailableRules(rootDir: File, wpDir: File) {
.map { it[2].replace("\\s+".toRegex(), "") to it[5] }
.forEach { ruleMap[it.first]!!.config = it.second}
val newText = File(wpDir, "sections/appendix.tex").readLines().toMutableList()
- newText.removeAll(newText.subList(newText.indexOf("\\section*{Available Rules}") + 1, newText.indexOf("\\lstMakeShortInline[basicstyle=\\ttfamily\\bfseries]`")))
+ newText.removeAll(newText.subList(newText.indexOf("\\section*{Available Rules}") + 1, newText.indexOf("%CodeStyle")))
var index = newText.indexOf("\\section*{Available Rules}") + 1
AUTO_TABLE.trimIndent().lines().forEach { newText.add(index++, it) }
ruleMap.map { it.value }
diff --git a/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/GenerationDocs.kt b/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/GenerationDocs.kt
index f3584d6b5e..483d8973d8 100644
--- a/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/GenerationDocs.kt
+++ b/info/buildSrc/src/main/kotlin/org/cqfn/diktat/generation/docs/GenerationDocs.kt
@@ -5,6 +5,7 @@ package org.cqfn.diktat.generation.docs
import java.io.File
import java.io.PrintWriter
+
/**
* Adds/updates diktat code style in white paper document.
*/
@@ -22,15 +23,14 @@ fun generateCodeStyle(guideDir: File, wpDir: File) {
val lines = file.readLines().toMutableList().drop(1)
tempFile.printWriter().use { writer ->
val iterator = lines.iterator()
- writer.writeln("\\lstMakeShortInline[basicstyle=\\ttfamily\\bfseries]`")
+ writer.writeWithoutApostrophe("%CodeStyle")
while (iterator.hasNext()) {
val line = iterator.next()
if (line.contains("## Preface"))
- break;
-
+ break
when {
line.startsWith("#") -> {
- writer.writeln("\\section*{${line.removePrefix("#").trim()}}")
+ writer.writeWithoutApostrophe("\\section*{${line.removePrefix("#").trim()}}")
}
line.startsWith("*") -> {
writeTableContentLine(writer, line, 0.5)
@@ -49,8 +49,7 @@ fun generateCodeStyle(guideDir: File, wpDir: File) {
if (line.contains("dw3cFWNRb$kiAU(sRYbjq*uItnDx9LFgp
zfB`>_Z*X5IpB8gW4dwls+%>r!5!pv4YVL)xXz2=MtY&>{n=HYh!QnX}t>Sw25do6<
zt6NP{64TSAz1?v{-+*JaV<(dCXKwxp%N{v)VXZc!H`@}bz3vO_HJw}pgr))7u@h(%dwNJTUPn&F0gmEb+RgK`bXzBmdCtgS@x7HMdVN#t~`d{GPWr~Qg+g%-_5!GHS<6E%0MGh!H!zF<3Y7zC3}
z-jzX=wahG0;DvUNmh^bUR$BOB%doZ#V^lsx+#-O}9J42(mj@BF{jM~G{6bhJCcf{|
zBENby@8LW?1%jiyHx1q|YqFV`v3HaX_G3CvK&sHUlUYZnN9vhp@&NTcaW2pxjuPt?
zX=$vny1MfyJqb$g2|};nVjG}gc1wGD(d9n%TP{_M90n{+7+Tm%v=-JaLR~mFgZ{gn
zv>EUM_`H#RGfc!PfH%%~fX(tb@LW?QbzqxC3jow*PIf>BBrX_NX~e8w+OV;?B=7CZ
zW|0!;XaO`Sez0bkBMiVb6*Mi=_(poc#x=!3-@ffb=t@##P3L^QQD
zSyw{$#-P19E>!dh8k}6;eXDgY-UWLhO9`Uk88jFEcdhBP;j+IJH%1icFvI?{^e9-L;fPqBG8p#LaDQY-W@
zadIj#aUcThg=xH6$h2;2e1-#t^L;7Q0Qxt{iV?2!J*$bU64h$F^}P&$N!bU<9z=v~
zHGlYwtRW#8REiB!K8}_`dQ{a|7OVktKuPY_Sn|SJ4NGpkq+pY3aj!(ygMA!nHQrU{
zpUvbguSypsf^d(mmBIH9H&p?yg%{mjIkm$*5c`Ehy2b2Ma$`)aOm+?zSr(1(37{>H
z#Nf(C$@GlCqKwYhsE)R>7IBpeY>
z!yGI8vI81QW1?TVfkcocZ7&R8NP4O&+)y*-D$A-_mIc`}NRaRc9j_gJ~tL;Ecuc)u9R2?n%ADPFhhep_nz
zfY4e}<%XIxNrmb@9xbetF(+%W2!5-@yfUfNUx>1fAy*H}B#g)TUe!+18AZ{lAG!NE
zeoOnx7!Xo-BQYpAGFa`9yf$r?W2skhqr9NTHsKy%+Qaogh?Hn_%1gH}HRIo)ct2z}
zWZ^4@-s42#vi~lVnqT9`>s{z!pv-Slp6Ul0Re8rI1Mo=_z4hjXhjZg?3$IH(m7YHO
zF2xjTwyrBstoTb$$$B2`p{3Smc51I#P}xgzV@CO)-VUd(_9KBmXSWm5hCw%Wa`u0JmkesU9F=+KDzfsmd
z8M{(BT^p9(tdR3&xBq!q);IBso%iOGszNQqGr^5?CdvTQc;AEJ^CQocO9XE}0k?Y<
zaH>S3RFMVxS#@+7MSO^-+s_S0J9wU7-vd_N$X(jdtQA4I`Pj=%AWUS1a9^cOfGIHk%i4Kx_AdOePYY06%P*xty5_>$BI$5rPP(Zeim{NvdRIl=^~*JMx`5pb
z=hS%WRt#RH;dSz_bGqtEVt2IzRY*@7)rx0AMpF#HxeGES9;%n!ph3U_@(t(dSTl`me
zJbYF@C0NmrG@f4F?8`#ZmSK|k1)Oq%E;BWz@Cc%s#N4_a#10uQ+TG}W*4&IZwa^I-
zUQt|+&CLcU{;EB%CX~-|F^kcGxCHyg4)xT9v(&s|+xOgeP#X(@_&kwF8qM`_qgtc#
zHSO|?fn{7WSeZ)HZYCXC`15)zfqRd(LN^Je`@%ZuaCkq08ujn#y@W|E|NRjqq
z02V0NYT>@Jb*ygtflP!Y^9buK>#2d+Nd@%XYfX*LtXdnI;3Urmf5p{ueP@!dkj}8%
z!lf+`2oyj7
z#Uefjv-IPJc2#>HQMj6%A}1XEXPxtUBJbK_s9xkazznguughleLCTx3#zlkbd}?hT
zU_RvP#^O8(yHEsQ6nBM6)e_n(^%_3Rt+Z3?MdP%mz+98kFk2K%gok2{LH@fLgJ_0w
z+0aqzW}aAV`Vb~QROVLrT#1T$89AriS|n~6SvuO*&TkW=j0OQY7atDWEttglTFSUL0#CKLV}=v(9?Nq)_`6RoY>IGr|*(nR(it}25=C9QJp
zkw)oq_{~tUhbu>9m(ti9jf9tyez88B?-Ey`J-e-k}448XV~hHYne
z9(&YyeF}k=H5+)s_^1+d2X(C_L8U#i=qz?3Ppwox>TZm#s2j5R
z7L>-7iZTrg5(#F5{jMR<$UPy!$@ahG!?S$4}y`(ea-
z5o8P3XllMn`Jatv)F{-*YejPA%ITvM;Lt^5To;3C
zmSud}`YH2M2fi?-`K^emKU=kxL#V}$fdsYIEhoK`Y{X4*8+;I?#q%C%8v=S8o&la&
zj7}B19~*@QnErvyt#aKokf+kA9uN6fMu``8@U-F_ch8=T4|5!*g(Yf9CAt4bK!CyvjT6-BI;W-q8YnW
z){3MOCo&EQV+bbrlE%!Oz?R8J}FH05}w)J8&)JGK+K*s|R
z;N4Sk3Q7RMg&&~FbnufI!fbBI>JY9k1{j(^Hq!q$xDOyzm>qrygXT0>!
zS8d7BIbwe+im_TFMkh!wik(mM(F)VuP@~-bdV+l1|aE#W#tm8
zHzqlASu$>`Jo3e*9TM6igU?MU&p0D9=}>4cGE=KtG`+=NYRE&tEq{?y4WTZS`pkHM
ze3T2ie^p7xl|v)-_^sz{8>;&c>B{Xz5w+}tz=e}4f3OgVY|=ESpj3KP<8^M^kARkU
zAf2e&-WLUneS4(}{u*7POQ@>&ZEBMwO9X5RiTv58QQW+C`~=MTDy17O+&TuI$v;P<
zb4+VutUZ+5Z}K86+H=Ts|4w?bF4;^T3Rtu{Mz6>IFjT8~q{H%(GRMy-oksy%ADm}~
zVVV9RzY?8R%bwUG;f%wh3R#E3mc?NyOpm3BMrtvtTHp2qWqsgj^T|T#aI;a6vP-Zf
z_x&e&%q#=AX1t_S!J=2>ntP}xV9M}%H8;L5hiD1h2jB1ZvNarpdlYI|stV64(jiU<
z-L2Z{i9$gHQWW}{2u*Y~x0AQ)7J^B?y3f`yniHsX4X0CJC(wS9o|d0@z<>{;Ysc_<
zPbGNA9J5!u5`T?{ZZaPa2p3BxE5w2ekC}qo%cDT5XM%5fngkfY1B&?14Q*?>|L3Kk
z$|rCP_k>mGNl|{2u}B=Q;;Qdz8$3kWUgA=OQ$xPyFCke;RXUXYpLv(68Y?7dUsQt
zM8-Eio3j{5p)ujxW&4uYx2jvaHFi+6;SWP0*G#=i
zcgr!?tLaHF_G6XWS=(0lc7aSAP%K1-S1GnBR)KAVA;lPg-*G-+dxzM5j=gC;+8^Ud
z=A2Ywv@J&&OmjAXWoqU5v}WjQ>e!o%CT-X^u4D3k?=^G5Hur)l^qma?_bM?(v{{(A
zsx7O=JN5!uQ(mc3=bi2~&JWjtJ!`|S2xPA1ab(o@o{W3&Zsx;(@(GeFzi7$irYRO+
zS}c?oXcw(1snLmUFca%Y^5
zOi5=&W#bZ6#_-edo5tw5O8$+?gNgjE`<%F{iC|Sk
zt&v<1EW_E6lYE=d*bHnFY}zEDlFC=m`O@WHFPw^;EN8umc+5EXSm|M4OylJXUTqc@
zdt8HW17$XLNFdmcZFn@4*OB76w-1u&F)ivLvHU}So4xZ@StI`&qn`n`6cHVp5hlP_
z4?XgynFT!VWaOn;)G22FDRZL)QJ7q?;30A1_!hk7(cdDCtreDUGq|ALw~POYGWzkb
z6`ML(%0Uaqv{^O9sp|$NGH@W+ePLzK`0z~J&zegZI8w#ldjiY9@|&2<6L%=tUEE^Y
zvBA!{E%he9k!+mX-L`B7al)S{J5stpq{NV0snD7j&YpueD(E*>_E&P-J=>wYYlgfZXa84hzM)LXn*Y8lA(h(Y4b@r#E5
zyivyJr?N{HGZh6ktz~^VHPB#{GbKk
z8!`4D%`u~M5xYRpFAgOL@cQp5GvzKq8<(XENp#Evcga+U4Hvr3&{V6tfDAx;^)K!~Y)q{4)xbeHB`^y8)>9r|z;wGr)&q)D1KHk*fy4cA$z$`(4EtWl
zv$J6+{x=B*pG8i=XJgc)$;G<033UE`f6^sJR|N%5
zSvzZy>;7WKD0H+Uu7o%b*Ev?l&ab5=!aen+fGIHbo?(C!mQ|bK8_`UmMG&SUHsyg4
z(b%!XTi$uGp`opzXsmLV-8^h+cqLt1`
zQ0LJwq7Psmb;XL$(UkGQO@B)$wQTuJ)FpfSkuzl(%ip2{I~Bzx$e>whguBevmye<-
zHnkyfhfMYV>n7y@MZO_ROD?>vu$2aHb{>PNFBUyXH9<&KNMd1@UpkL@ClfoqeUq$
z;!)xYpws9vHLN4f-m+IA+q20O%aOH1O$*b@k4GrovGBDIPOE6M@6vs
zDVd0V=(aR{M8R1l`wU#S>yPwQ!k;7&d*4rvO93=kT1cOu883<|)Z4k(Gy+zp%U<>W
zp#;Eq4x@6}9$}i^%z_6G%SzfmNru`82FJF=KO)iRTWqc&c^r;He&H~i(t&Q}r=#$G
z-y3W#vfpgcLNfE}z_E@<6tB_FLTVQlg0!KuWz~Hxsbd5#Z24ATi3D@`jAg3+S$28Rkq3#`JhY>C?tM1
zsQnc54vVO^(jc&YKamHY(6OoNl!YA@^CPR!Sng#O%UY8CSO}WyDTHxWzfK9R@#ay*ozsm3+hXvLf)D?xEAO1pV@|Jp);emZ
zHR_TN5MIj!wLy?t_}G7w=F$edJFER+`d3~0a~WLK!ZGLO15?-o70VD!!#}``SQAp&
z`G(M|n~jXE!CU@yvm4rq!`73i=@I}5^owV
z6FP`|Q3Wl<&Q!&GaLTJWfa2^1aP-}LC^nf3lj7vJUY5K|PR_$N%eOn*zD@88m24m@
zIUW&Uk2+gXJHT+1GW0xshhs`#U9(?NrTv~tN6UdWMP>Sl6ZC~6xtNh(4=rxK
z`dLmP=k4=pd-^HozO&$>)Xz_2(BDnwXK7$c1&FE$7SN;@43MfW=FtErmSRem&H+@n
zLDZXz{&jJ1*j8dU4qnO%T9$&V=4~n>DBH|;>=8Q;OXM(#0(uZxrS4&
zb`iX!Og6^6`HwQw>k#*ldoO|Ptl0a|{4)(}6}$rxT&xi!KPYH;P3vfu=DXr7y9MB~
z(GA7r$!zHah<%>F7LtGaJ;Ord_*IZgWvS*PPw|NOPQU(L%G0T-A%qqjyKhi?H|VaF
z(t#Z7f15JwEVfpu$IRzo)p@NtVAh)Tqp1KD@v*e4T9SqXmJw4TK0b%!)i_&>S={NK
z86UGy<0|_{EZR*wC06|O!L^Xj=YVGK=_RFE7-HJu0VfOO1@i~R%%x;!3&5N4F32Is
zOU3TnCvrL(F&l)Ss*y%T@U+1wL}$`Hm=L6b-E`i%7wq>>5w;
z>gY&>YuiW<&qMU%hS|gQT|5KM>-7panfdOjAgfB7_(okv+Pd%671pfx!f^3m3be>-
zIeRs_rlsjPSMZuaL4dVi1axr7nZ%9QT8N3R-1M$l+ixc_Hj_Sjc49L?re?3Xt4~Q~dSgko
z^NOc%p}rFO!nf~=4Dc_j-*hOHtbd2Yb@dq#lMp+Cpf&-Zd~C8S`w0_m1sJ=D00-MK
z!;98mr7Lu4FJ+;A#Y!~-vqrIs40sk3UKZ9UlEHml7=cS=$^;!QsqRt3W}~hIy`sd_
z+KdMzL<8?<6~~Dg(F_XHQ=vrV>7`4
zeq+NEo;)aSbsa9jMBa0erZ)Ky@*6IV*IY28WA)QYjf;ny2t(w&@X*B!V1rcl`geAo
z?y5j>81s|RgbXs}08KupjXhwFe!GA^)Ckwez5w(@89mRfd-1tdjr-5;Dr|x
z=NO8hcK{%Ch;OV#1FwyH0F5W*SxM1wto5qQSz>XP<&N@~%87!uFXPMfn7&z1FrEz1
zmtf9(c({=%K2Ri?KswHCvFpYtW+qCWAIX9}t`qu*YG)VnV$*u%D%n}_bnmbGK?GS>
zIT8#sDN)6TU4yv}@fX89kN&a~9SXPyPRv^PV+kPWY|4CV+V+l=xzujuvLmBa4bLEpd%bEi
zqGP~H_0$*|DS9JBqTfl1iTqPgt3jKt^4kVK>aPYic8j2f)j?uD+{#4jd|vqMDm^t6
z3=65D?*bv~jaUvT0+A?5a_x(=9o+5kZ>KmlTk`l72#3Tk({of5d;zn=KS)FNI3vw5
zjPBbwr=H=!<{mdJb*a%3l7OQEJger;0IwhQo&5yCMT`tBZFAF<42YsLF3{JcJ}~|z
zWd<=&2)8TUR&n~%kfS_Bg6mmJwEtTRC1V}tMFjjKQ`fLuhO?J>A@wXP3^cK0-^QlE
zDp}If0dAdO@*PGP3H59fcrTEXlw)D*v;06Ji{)o^M_=j$bwnlD5VPUx?h7T2iQtM$
z)(wXk;8_1=FInRcp6D?aL~$TdvXh^V8qXt}aTbzW993#%e`Y=u1$I|>Nesi34uH!p
z<}kObT)~o~)L}}eiMJ!O=i^=hjYbUG4;C3*fr)LU1rewHot^?3p=AJ)fNMFG$wnj!OR1p)#a0;0oX<3-^AuiAY!OPd
zsJ(_MuGXW9Q8&!6Ba(cr8Ku=63?1Vd+l5doyoE?Mm~%)`vH9p_=c$euel=coQ%opU
z^`g$iv01dx$dZ*bNhA|ULSb6_u4xbMjpptimusC9thLH=yOBuyTm}9`aZFrGsyDhm
z4fuD?i;w`2lqhvc`&hB>b_kiZV@aq#?nv$ts>$uls&mql@YiU#aH%R0a2FOzPqnZa
z8oQlgP{`TR!EEtcnJ10-0N%XM042Z)`L^^qaX?_QC%AK8`Vvq+p8`^f2{+-{)7WuD
z)#9gWz2ecC-W4P1aEl`|A6XO>sv+OEFKr}C0W3R)j#47Qu7v0ir}$h>;mg|`nUf5>
zD-M9vNU;DTgRcoc#ILTO=psmE4&Ux(s08Z0uRH2klw~
zzqa~IX*m&YxjLBVh`$Pj3yt4ahHmGr(AZk$O_bp}>9`r*xVC`N7torte|jEiX$M79
z2VsF6$T+fgyZzOL3c1$sV!irdyX^9%=?oM`3!b(aH+gEt5fc_m0#V3I`P$jZCRm{+
z8L(??b>>p&iMkXOK94P2-HQ-uw^eEP<2V46BMqo&GVUKme8;Ta%gIdl4q@@pBq|Mi
zn2K9pU4{&(=T3tD+W?s@a|wsww^ZJZ-FLW_lVkugx^a+ZXW|E<{5V%5b#C%WknpY~
z#UJgvcjLc=Bh~l|LefX7T23B2Ba#>5Ojab)ucE(?B?H8u=BP`VA8{f|JIX16HA5=mWm3Pch0IcZXjv}{67yg#ouPI<
zpfK7t9deLk`)|T3r$1wk<|;Cs5G@4YeFdi
z8pOiQ*1c^SfgFAGmkt$sz-j{cQ4)ebq5c8poC5P%OHcoJ%pqJb!_LBT8{#}Jx@;|S
zqr|E66MKKIDR}{6M6PVGeyz&ofW(wT^7D_uQn{->0YPVKkh6$YPQQFL2$%J*`P?7f-Hm-M^Dx=3
zbF|P*?mZ3!%SU_(**6M}cA^{gr9|{&TUU*BFF@GlRu!u>2aqizl&odfS_=A;FLKPt
z;cfRC4XP57vtlh7h@>Ac>=jD_h`1BS=VV4+$~z37em+qW!erjc-rFq)|7h^oX7&C%
za_WHex$MWH9_z3ZCJ7=fYyx;5CjCm+s_-qQKJF=q=aUb>w`NLpEp>KVOuY&IZCNsxztE|>w)fa*@f<@jL<2n?
z+Mi-dZ?vX((yq=8@LT{zq^mBr3dda3`i96|`@9GmEnod0Hv7)m&)8HDz8pfxwYA$|)Tw|7Y)CE-c?mv+s$ygu8=x_o2WuV-+k>
z@Z>FfNdpoX{5{@YPf+7u$_eE^aV_(P0C)Q=--eV!2YbE7`4dQ?saYO9Td-TBx4;MJ
z6R!c--AXky?vf7b6sX8(0R)DciVh)$48YO~B3Cfn60o_rl0vRD0
z-+Th9rFoK3{>VA)x8^~4%qP_)!>h4CymI`@$Cj-*p`s4ik<%Mv<+Y!}%WMjWX-dj7wP0grV
z<+4(FO2DP8Yor77(Iuf$%K}m$9`U>7WL#&|x|pLDn_EzBY$;Eu91}eEMV#@;J#*p=
zJjKHb2geh+og+TqDnTJ-PhyOX4@C<-X3i>Al=O`LD>(VC?HL|~a=4^3v7rOrQALje
zqFv~`iYGJ>P!Z%?R@};^b2cHAdebC#)bG{iTN|r3N?{1p;epcZ(qr#f)Kw0@wKn;b
z8okrK{jXFxHievB#Tj&_lpGzaK9gKB7fvH@``X|g`&|k}AeP(LKlZ}ir`jqSbj|=N
zzXYzF{|;pf_CBcq5cf>9rbx4S2+Mm=pfmLuv=e#NN2-(O!bwxLM+k?C3@1|`RuhR&
zNufc*b$BBqU7#Z3Gpgt<*k>TIx|eVRjg(y!;p&vVbkkODOiB%}V2%rjmESE@+o;&?
z?Yqj?Sq}%I@|*=e#vG?SRKXpLt;h0>G*G@4c}Pqk%UU}t=LtNz`(ErI+|c(TKf6Or
zTx%Nu^*K|8&TZ$$ZHwZJT=UO8$N*rGoVb;1b_s}n^&a}+>vlcA{44ZrwOjSWnIGM}
zU&y$1st_$QXY-_WqqsP>;a8+Ys$Cr7OKRHhsS>M14r06*vH7MMCy|BE4ILqpel@Qf
z5LYc>jEG_bi;+y=(-aC8flU|XtloJ3L5ceY6YcGzAoeGG