Repository: analysis-dev/diktat (kotlin)

Issue:
Trailing comma: new feature from Kotlin 1.4
Kotlin 1.4 introduce the trailing comma feature. So we need to support it now not only for ENUMs as we did it before, but our code-style should force people to do this. For example:
```
fun foo(
    a: Int,
    b: String, // <--- this trailing comma is extremely useful for merging conflicts and swapping easily line without changing them
) 

val colors = listOf(
    "red",
    "green",
    "blue", //trailing comma
)
```

same should be done for function calls/constructor arguments/e.t.c

Kotlin supports trailing commas in the following cases:

Enumerations
Value arguments
Class properties and parameters
Function value parameters
Parameters with optional type (including setters)
Indexing suffix
Lambda parameters
when entry
Collection literals (in annotations)
Type arguments
Type parameters
Destructuring declarations

Pull request:
### What's done:
   Created new rule, added tests

## Actions checklist
* [x] Implemented Rule, added Warnings
* [x] Added tests on checks
* [x] Added tests on fixers
* [x] Updated diktat-analysis.yml
* [x] Updated available-rules.md
* [x] closes #419

Patch:
diff --git a/diktat-analysis.yml b/diktat-analysis.yml
index 970cd8a055..b728e8d185 100644
--- a/diktat-analysis.yml
+++ b/diktat-analysis.yml
@@ -232,6 +232,26 @@
     maxParametersInOneLine: 2
     # 3 by default.
     # maxCallsInOneLine: 3
+# Checks trailing comma
+- name: TRAILING_COMMA
+  enabled: true
+  configuration:
+    # VALUE_ARGUMENT
+    valueArgument: false
+    # VALUE_PARAMETER
+    valueParameter: false
+    # REFERENCE_EXPRESSION
+    indices: false
+    # WHEN_CONDITION_WITH_EXPRESSION
+    whenConditions: false
+    # STRING_TEMPLATE
+    collectionLiteral: false
+    # TYPE_PROJECTION
+    typeArgument: false
+    # TYPE_PARAMETER
+    typeParameter: false
+    # DESTRUCTURING_DECLARATION_ENTRY
+    destructuringDeclaration: false
 # Inspection that checks if a long dot qualified expression is used in condition or as an argument
 - name: COMPLEX_EXPRESSION
   enabled: true
diff --git a/diktat-rules/src/main/kotlin/generated/WarningNames.kt b/diktat-rules/src/main/kotlin/generated/WarningNames.kt
index e8043f0aee..b26940d76c 100644
--- a/diktat-rules/src/main/kotlin/generated/WarningNames.kt
+++ b/diktat-rules/src/main/kotlin/generated/WarningNames.kt
@@ -145,6 +145,8 @@ public object WarningNames {
 
     public const val WRONG_NEWLINES: String = "WRONG_NEWLINES"
 
+    public const val TRAILING_COMMA: String = "TRAILING_COMMA"
+
     public const val COMPLEX_EXPRESSION: String = "COMPLEX_EXPRESSION"
 
     public const val STRING_CONCATENATION: String = "STRING_CONCATENATION"
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 48347021b6..6da026600b 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
@@ -99,6 +99,7 @@ enum class Warnings(
     LONG_LINE(true, "3.5.1", "this line is longer than allowed"),
     REDUNDANT_SEMICOLON(true, "3.6.2", "there should be no redundant semicolon at the end of lines"),
     WRONG_NEWLINES(true, "3.6.2", "incorrect line breaking"),
+    TRAILING_COMMA(true, "3.6.2", "use trailing comma"),
     COMPLEX_EXPRESSION(false, "3.6.3", "complex dot qualified expression should be replaced with variable"),
 
     // FixMe: autofixing will be added for this rule
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 d5e5c0245e..5c247ad108 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
@@ -28,6 +28,7 @@ import org.cqfn.diktat.ruleset.rules.chapter3.SingleLineStatementsRule
 import org.cqfn.diktat.ruleset.rules.chapter3.SortRule
 import org.cqfn.diktat.ruleset.rules.chapter3.StringConcatenationRule
 import org.cqfn.diktat.ruleset.rules.chapter3.StringTemplateFormatRule
+import org.cqfn.diktat.ruleset.rules.chapter3.TrailingCommaRule
 import org.cqfn.diktat.ruleset.rules.chapter3.WhenMustHaveElseRule
 import org.cqfn.diktat.ruleset.rules.chapter3.files.BlankLinesRule
 import org.cqfn.diktat.ruleset.rules.chapter3.files.FileSize
@@ -167,6 +168,7 @@ class DiktatRuleSetProvider(private var diktatConfigFile: String = DIKTAT_ANALYS
             ::AvoidUtilityClass,
             ::PropertyAccessorFields,
             ::AbstractClassesRule,
+            ::TrailingCommaRule,
             ::SingleInitRule,
             ::CustomLabel,
             ::VariableGenericTypeDeclarationRule,
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/TrailingCommaRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/TrailingCommaRule.kt
new file mode 100644
index 0000000000..cfdeb9b962
--- /dev/null
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/TrailingCommaRule.kt
@@ -0,0 +1,132 @@
+package org.cqfn.diktat.ruleset.rules.chapter3
+
+import org.cqfn.diktat.common.config.rules.RuleConfiguration
+import org.cqfn.diktat.common.config.rules.RulesConfig
+import org.cqfn.diktat.common.config.rules.getCommonConfiguration
+import org.cqfn.diktat.common.config.rules.getRuleConfig
+import org.cqfn.diktat.ruleset.constants.EmitType
+import org.cqfn.diktat.ruleset.constants.Warnings.TRAILING_COMMA
+import org.cqfn.diktat.ruleset.utils.log
+
+import com.pinterest.ktlint.core.Rule
+import com.pinterest.ktlint.core.ast.ElementType.COLLECTION_LITERAL_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.COMMA
+import com.pinterest.ktlint.core.ast.ElementType.DESTRUCTURING_DECLARATION
+import com.pinterest.ktlint.core.ast.ElementType.DESTRUCTURING_DECLARATION_ENTRY
+import com.pinterest.ktlint.core.ast.ElementType.INDICES
+import com.pinterest.ktlint.core.ast.ElementType.REFERENCE_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.STRING_TEMPLATE
+import com.pinterest.ktlint.core.ast.ElementType.TYPE_ARGUMENT_LIST
+import com.pinterest.ktlint.core.ast.ElementType.TYPE_PARAMETER
+import com.pinterest.ktlint.core.ast.ElementType.TYPE_PARAMETER_LIST
+import com.pinterest.ktlint.core.ast.ElementType.TYPE_PROJECTION
+import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT
+import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST
+import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER
+import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER_LIST
+import com.pinterest.ktlint.core.ast.ElementType.WHEN_CONDITION_IN_RANGE
+import com.pinterest.ktlint.core.ast.ElementType.WHEN_CONDITION_IS_PATTERN
+import com.pinterest.ktlint.core.ast.ElementType.WHEN_CONDITION_WITH_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.WHEN_ENTRY
+import com.pinterest.ktlint.core.ast.children
+import com.pinterest.ktlint.core.ast.isPartOfComment
+import com.pinterest.ktlint.core.ast.isWhiteSpaceWithNewline
+import org.jetbrains.kotlin.com.intellij.lang.ASTNode
+import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
+import org.jetbrains.kotlin.psi.psiUtil.siblings
+
+/**
+ * [1] Enumerations (In another rule)
+ * [2] Value arguments
+ * [3] Class properties and parameters
+ * [4] Function value parameters
+ * [5] Parameters with optional type (including setters)
+ * [6] Indexing suffix
+ * [7] Lambda parameters
+ * [8] when entry
+ * [9] Collection literals (in annotations)Type arguments
+ * [10] Type arguments
+ * [11] Type parameters
+ * [12] Destructuring declarations
+ */
+@Suppress("TOO_LONG_FUNCTION")
+class TrailingCommaRule(private val configRules: List<RulesConfig>) : Rule("trailing-comma") {
+    private var isFixMode: Boolean = false
+    private val commonConfig by configRules.getCommonConfiguration()
+    private val trailingConfig = this.configRules.getRuleConfig(TRAILING_COMMA)?.configuration ?: emptyMap()
+    private val configuration by lazy {
+        if (trailingConfig.isEmpty()) {
+            log.warn("You have enabled TRAILING_COMMA, but rule will remain inactive until you explicitly set" +
+                    " configuration options. See [available-rules.md] for possible configuration options.")
+        }
+        TrailingCommaConfiguration(trailingConfig)
+    }
+    private lateinit var emitWarn: EmitType
+
+    override fun visit(
+        node: ASTNode,
+        autoCorrect: Boolean,
+        emit: EmitType
+    ) {
+        emitWarn = emit
+        isFixMode = autoCorrect
+
+        if (commonConfig.kotlinVersion >= ktVersion) {
+            val (type, config) = when (node.elementType) {
+                VALUE_ARGUMENT_LIST -> Pair(VALUE_ARGUMENT, configuration.getParam("valueArgument"))
+                VALUE_PARAMETER_LIST -> Pair(VALUE_PARAMETER, configuration.getParam("valueParameter"))
+                INDICES -> Pair(REFERENCE_EXPRESSION, configuration.getParam("referenceExpression"))
+                WHEN_ENTRY -> {
+                    val lastChildType = node
+                        .children()
+                        .toList()
+                        .findLast { it.elementType in whenChildrenTypes }
+                        ?.elementType
+                    Pair(lastChildType, configuration.getParam("whenConditions"))
+                }
+                COLLECTION_LITERAL_EXPRESSION -> Pair(STRING_TEMPLATE, configuration.getParam("collectionLiteral"))
+                TYPE_ARGUMENT_LIST -> Pair(TYPE_PROJECTION, configuration.getParam("typeArgument"))
+                TYPE_PARAMETER_LIST -> Pair(TYPE_PARAMETER, configuration.getParam("typeParameter"))
+                DESTRUCTURING_DECLARATION -> Pair(
+                    DESTRUCTURING_DECLARATION_ENTRY,
+                    configuration.getParam("destructuringDeclaration")
+                )
+                else -> return
+            }
+            val astNode = node
+                .children()
+                .toList()
+                .lastOrNull { it.elementType == type }
+            astNode?.checkTrailingComma(config)
+        }
+    }
+
+    private fun ASTNode.checkTrailingComma(config: Boolean) {
+        val shouldFix = this.siblings(true).toList().run {
+            !this.map { it.elementType }.contains(COMMA) && this.find { it.isWhiteSpaceWithNewline() || it.isPartOfComment() } != null
+        }
+        if (shouldFix && config) {
+            // we should write type of node in warning, to make it easier for user to find the parameter
+            TRAILING_COMMA.warnAndFix(configRules, emitWarn, isFixMode, "after ${this.elementType}: ${this.text}", this.startOffset, this) {
+                val parent = this.treeParent
+                parent.addChild(LeafPsiElement(COMMA, ","), this.treeNext)
+            }
+        }
+    }
+
+    /**
+     * Configuration for trailing comma
+     */
+    class TrailingCommaConfiguration(config: Map<String, String>) : RuleConfiguration(config) {
+        /**
+         * @param name parameters name
+         * @return param based on its name
+         */
+        fun getParam(name: String) = config[name]?.toBoolean() ?: false
+    }
+
+    companion object {
+        val ktVersion = KotlinVersion(1, 4)
+        val whenChildrenTypes = listOf(WHEN_CONDITION_WITH_EXPRESSION, WHEN_CONDITION_IS_PATTERN, WHEN_CONDITION_IN_RANGE)
+    }
+}
diff --git a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml
index 851b5ff10e..089100ce03 100644
--- a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml
+++ b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml
@@ -232,6 +232,26 @@
     maxParametersInOneLine: 2
     # 3 by default.
     # maxCallsInOneLine: 3
+# Checks trailing comma
+- name: TRAILING_COMMA
+  enabled: true
+  configuration:
+    # VALUE_ARGUMENT
+    valueArgument: false
+    # VALUE_PARAMETER
+    valueParameter: false
+    # REFERENCE_EXPRESSION
+    indices: false
+    # WHEN_CONDITION_WITH_EXPRESSION
+    whenConditions: false
+    # STRING_TEMPLATE
+    collectionLiteral: false
+    # TYPE_PROJECTION
+    typeArgument: false
+    # TYPE_PARAMETER
+    typeParameter: false
+    # DESTRUCTURING_DECLARATION_ENTRY
+    destructuringDeclaration: false
 # Checks that there are not too many consecutive spaces in line
 - name: TOO_MANY_CONSECUTIVE_SPACES
   enabled: true
diff --git a/diktat-rules/src/main/resources/diktat-analysis.yml b/diktat-rules/src/main/resources/diktat-analysis.yml
index e950b05411..ead655ea98 100644
--- a/diktat-rules/src/main/resources/diktat-analysis.yml
+++ b/diktat-rules/src/main/resources/diktat-analysis.yml
@@ -228,6 +228,26 @@
     maxParametersInOneLine: 2
     # 3 by default.
     # maxCallsInOneLine: 3
+# Checks trailing comma
+- name: TRAILING_COMMA
+  enabled: true
+  configuration:
+    # VALUE_ARGUMENT
+    valueArgument: false
+    # VALUE_PARAMETER
+    valueParameter: false
+    # REFERENCE_EXPRESSION
+    indices: false
+    # WHEN_CONDITION_WITH_EXPRESSION
+    whenConditions: false
+    # STRING_TEMPLATE
+    collectionLiteral: false
+    # TYPE_PROJECTION
+    typeArgument: false
+    # TYPE_PARAMETER
+    typeParameter: false
+    # DESTRUCTURING_DECLARATION_ENTRY
+    destructuringDeclaration: false
 # Checks that there are not too many consecutive spaces in line
 - name: TOO_MANY_CONSECUTIVE_SPACES
   enabled: true
diff --git a/info/available-rules.md b/info/available-rules.md
index 685354dd3b..1f2e74d997 100644
--- a/info/available-rules.md
+++ b/info/available-rules.md
@@ -69,6 +69,7 @@
 |    3  |     3.6.2  |  REDUNDANT_SEMICOLON                      |  Check: warns if semicolons are used at the end of line.<br>Fix: removes semicolon.                                                                                            |  yes  |  no  |                                                                                      - |
 |    3  |     3.6.2  |  WRONG_NEWLINES                           |  Check: warns if line breaks do not follow code style guid.<br>Fix: fixes incorrect line breaks.                                                                               |  yes  |  no  |                                                                                      - |
 |    3  |     3.6.2  |  COMPLEX_EXPRESSION                       |  Check: warns if a long dot qualified expression is used in condition or as an argument                                                                                        |  no   |  no  | - |
+|    3  |     3.6.2  |  TRAILING_COMMA                           |  Check: warns if missing trailing comma                                                                                                                                        |  yes   |  valueArgument valueParameter indices whenConditions collectionLiteral typeArgument typeParameter destructuringDeclaration  | - |
 |    3  |    3.15.1  |  STRING_CONCATENATION                     |  Check: warns if in a single line concatenation of strings is used                                                                                                             |  yes  |  no  |  - |
 |    3  |     3.7.1  |  TOO_MANY_BLANK_LINES                     |  Check: warns if blank lines are used placed incorrectly.<br>Fix: removes redundant blank lines.                                                                               |  yes  |  no  |              |
 |    3  |     3.8.1  |  WRONG_WHITESPACE                         |  Check: warns if usage of horizontal spaces violates code style guide.<br>Fix: fixes incorrect whitespaces.                                                                    |  yes  |  no  |                                                                                      - |
diff --git a/info/guide/guide-chapter-3.md b/info/guide/guide-chapter-3.md
index 9167ff0ab3..e764e325af 100644
--- a/info/guide/guide-chapter-3.md
+++ b/info/guide/guide-chapter-3.md
@@ -504,6 +504,23 @@ fun foo(
 }
  ```
 
+same should be done for function calls/constructor arguments/e.t.c
+
+Kotlin supports trailing commas in the following cases:
+
+Enumerations
+Value arguments
+Class properties and parameters
+Function value parameters
+Parameters with optional type (including setters)
+Indexing suffix
+Lambda parameters
+when entry
+Collection literals (in annotations)
+Type arguments
+Type parameters
+Destructuring declarations
+
 8) If the supertype list has more than two elements, they should be separated by newlines.
 
 **Valid example:** 


Test patch:
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/TrailingCommaFixTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/TrailingCommaFixTest.kt
new file mode 100644
index 0000000000..226b478ca2
--- /dev/null
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/TrailingCommaFixTest.kt
@@ -0,0 +1,35 @@
+package org.cqfn.diktat.ruleset.chapter3
+
+import org.cqfn.diktat.common.config.rules.DIKTAT_COMMON
+import org.cqfn.diktat.common.config.rules.RulesConfig
+import org.cqfn.diktat.ruleset.constants.Warnings
+import org.cqfn.diktat.ruleset.rules.chapter3.TrailingCommaRule
+import org.cqfn.diktat.util.FixTestBase
+
+import generated.WarningNames
+import org.junit.jupiter.api.Tag
+import org.junit.jupiter.api.Test
+
+class TrailingCommaFixTest : FixTestBase("test/paragraph3/trailing_comma", ::TrailingCommaRule) {
+    private val config: List<RulesConfig> = listOf(
+        RulesConfig(
+            Warnings.TRAILING_COMMA.name, true,
+            mapOf("valueArgument" to "true",
+                "valueParameter" to "true",
+                "indices" to "true",
+                "whenConditions" to "true",
+                "collectionLiteral" to "true",
+                "typeArgument" to "true",
+                "typeParameter" to "true",
+                "destructuringDeclaration" to "true")),
+        RulesConfig(
+            DIKTAT_COMMON, true,
+            mapOf("kotlinVersion" to "1.4.21"))
+    )
+
+    @Test
+    @Tag(WarningNames.TRAILING_COMMA)
+    fun `should add all trailing comma`() {
+        fixAndCompare("TrailingCommaExpected.kt", "TrailingCommaTest.kt", config)
+    }
+}
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/TrailingCommaWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/TrailingCommaWarnTest.kt
new file mode 100644
index 0000000000..829ef16f3f
--- /dev/null
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/TrailingCommaWarnTest.kt
@@ -0,0 +1,289 @@
+package org.cqfn.diktat.ruleset.chapter3
+
+import org.cqfn.diktat.common.config.rules.RulesConfig
+import org.cqfn.diktat.ruleset.constants.Warnings.TRAILING_COMMA
+import org.cqfn.diktat.ruleset.rules.DIKTAT_RULE_SET_ID
+import org.cqfn.diktat.ruleset.rules.chapter3.TrailingCommaRule
+import org.cqfn.diktat.util.LintTestBase
+
+import com.pinterest.ktlint.core.LintError
+import generated.WarningNames
+import org.junit.jupiter.api.Tag
+import org.junit.jupiter.api.Test
+
+class TrailingCommaWarnTest : LintTestBase(::TrailingCommaRule) {
+    private val ruleId = "$DIKTAT_RULE_SET_ID:trailing-comma"
+
+    private fun getRulesConfig(paramName: String): List<RulesConfig> = listOf(
+        RulesConfig(
+            TRAILING_COMMA.name, true,
+            mapOf(paramName to "true"))
+    )
+
+    @Test
+    @Tag(WarningNames.TRAILING_COMMA)
+    fun `check value arguments`() {
+        lintMethod(
+            """
+                fun shift(x: Int, y: Int) { 
+                    shift(
+                        25,
+                        20 // trailing comma
+                    )
+                    
+                    val colors = listOf(
+                        "red",
+                        "green",
+                        "blue" // trailing comma
+                    )
+                }
+                """.trimMargin(),
+            LintError(4, 25, ruleId, "${TRAILING_COMMA.warnText()} after VALUE_ARGUMENT: 20", true),
+            LintError(10, 25, ruleId, "${TRAILING_COMMA.warnText()} after VALUE_ARGUMENT: \"blue\"", true),
+            rulesConfigList = getRulesConfig("valueArgument")
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.TRAILING_COMMA)
+    fun `check class properties and parameters`() {
+        lintMethod(
+            """
+                class Customer(
+                    val name: String,
+                    val lastName: String // trailing comma
+                )
+            
+                class Customer(
+                    val name: String,
+                    lastName: String // trailing comma
+                )
+                """.trimMargin(),
+            LintError(3, 21, ruleId, "${TRAILING_COMMA.warnText()} after VALUE_PARAMETER: val lastName: String // trailing comma", true),
+            LintError(8, 21, ruleId, "${TRAILING_COMMA.warnText()} after VALUE_PARAMETER: lastName: String // trailing comma", true),
+            rulesConfigList = getRulesConfig("valueParameter")
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.TRAILING_COMMA)
+    fun `check function value parameters`() {
+        lintMethod(
+            """
+                class A {
+                
+                    fun foo() {}
+                    
+                    fun powerOf(
+                        number: Int, 
+                        exponent: Int, // trailing comma
+                    ) { /*...*/ }
+                    
+                    constructor(
+                        x: Comparable<Number>,
+                        y: Iterable<Number>
+                    ) {}
+                    
+                    fun print(
+                        vararg quantity: Int,
+                        description: String
+                    ) {}
+                }
+                """.trimMargin(),
+            LintError(12, 25, ruleId, "${TRAILING_COMMA.warnText()} after VALUE_PARAMETER: y: Iterable<Number>", true),
+            LintError(17, 25, ruleId, "${TRAILING_COMMA.warnText()} after VALUE_PARAMETER: description: String", true),
+            rulesConfigList = getRulesConfig("valueParameter")
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.TRAILING_COMMA)
+    fun `check parameters with optional type`() {
+        lintMethod(
+            """
+                fun foo() {
+                    val sum: (Int, Int, Int,) -> Int = fun(
+                        x,
+                        y,
+                        z // trailing comma
+                    ): Int {
+                        return x + y + x
+                    }
+                    println(sum(8, 8, 8))
+                }
+                """.trimMargin(),
+            LintError(5, 25, ruleId, "${TRAILING_COMMA.warnText()} after VALUE_PARAMETER: z // trailing comma", true),
+            rulesConfigList = getRulesConfig("valueParameter")
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.TRAILING_COMMA)
+    fun `check indexing suffix`() {
+        lintMethod(
+            """
+                class Surface {
+                    operator fun get(x: Int, y: Int) = 2 * x + 4 * y - 10
+                }
+                fun getZValue(mySurface: Surface, xValue: Int, yValue: Int) =
+                    mySurface[
+                        xValue,
+                        yValue // trailing comma
+                    ]
+                """.trimMargin(),
+            LintError(7, 25, ruleId, "${TRAILING_COMMA.warnText()} after REFERENCE_EXPRESSION: yValue", true),
+            rulesConfigList = getRulesConfig("referenceExpression")
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.TRAILING_COMMA)
+    fun `check lambda parameters`() {
+        lintMethod(
+            """
+                fun main() {
+                    val x = {
+                            x: Comparable<Number>,
+                            y: Iterable<Number> 
+                            -> println("1",)
+                    }
+                
+                    println(x,)
+                }
+                """.trimMargin(),
+            rulesConfigList = getRulesConfig("valueParameter")
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.TRAILING_COMMA)
+    fun `check when entry`() {
+        lintMethod(
+            """
+                fun isReferenceApplicable(myReference: KClass<*>) = when (myReference) {
+                    Comparable::class,
+                    Iterable::class,
+                    String::class // trailing comma
+                        -> true
+                    else -> false
+                }
+                                
+                fun someFun() {
+                   when (x) {
+                       is Int,
+                       is String
+                            -> print((x as Int).length)
+                       is Long, -> x as Int
+                   }
+               }
+               
+               fun someFun() {
+                   when (x) {
+                       in 1..2
+                        -> foo()
+                   }
+               }
+               
+               fun someFun() {
+                   when (x) {}
+               }
+                """.trimMargin(),
+            LintError(4, 21, ruleId, "${TRAILING_COMMA.warnText()} after WHEN_CONDITION_WITH_EXPRESSION: String::class", true),
+            LintError(12, 24, ruleId, "${TRAILING_COMMA.warnText()} after WHEN_CONDITION_IS_PATTERN: is String", true),
+            LintError(20, 24, ruleId, "${TRAILING_COMMA.warnText()} after WHEN_CONDITION_IN_RANGE: in 1..2", true),
+            rulesConfigList = getRulesConfig("whenConditions")
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.TRAILING_COMMA)
+    fun `check collection literals`() {
+        lintMethod(
+            """
+                annotation class ApplicableFor(val services: Array<String>)
+                
+                @ApplicableFor([
+                    "serializer",
+                    "balancer",
+                    "database",
+                    "inMemoryCache" // trailing comma
+                ],)
+                fun foo() {}
+                """.trimMargin(),
+            LintError(7, 21, ruleId, "${TRAILING_COMMA.warnText()} after STRING_TEMPLATE: \"inMemoryCache\"", true),
+            rulesConfigList = getRulesConfig("collectionLiteral")
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.TRAILING_COMMA)
+    fun `check type arguments`() {
+        lintMethod(
+            """
+                fun <T1, T21,> foo() {}
+                
+                fun main() {
+                    foo<
+                            Comparable<Number,>,
+                            Iterable<Number
+                            > // trailing comma
+                            >()
+                }
+                """.trimMargin(),
+            LintError(6, 29, ruleId, "${TRAILING_COMMA.warnText()} after TYPE_PROJECTION: Iterable<Number...", true),
+            LintError(6, 38, ruleId, "${TRAILING_COMMA.warnText()} after TYPE_PROJECTION: Number", true),
+            rulesConfigList = getRulesConfig("typeArgument")
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.TRAILING_COMMA)
+    fun `check type parameters`() {
+        lintMethod(
+            """
+                class MyMap<
+                        MyKey,
+                        MyValue // trailing comma
+                        > {}
+                """.trimMargin(),
+            LintError(3, 25, ruleId, "${TRAILING_COMMA.warnText()} after TYPE_PARAMETER: MyValue", true),
+            rulesConfigList = getRulesConfig("typeParameter")
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.TRAILING_COMMA)
+    fun `check destructuring declarations`() {
+        lintMethod(
+            """
+                fun foo() {
+                    data class Car(val manufacturer: String, val model: String, val year: Int)
+                    val myCar = Car("Tesla", "Y", 2019)
+                    
+                    val (
+                        manufacturer,
+                        model,
+                        year // trailing comma
+                    ) = myCar
+                    
+                    val cars = listOf<Car>()
+                    fun printMeanValue() {
+                        var meanValue: Int = 0
+                        for ((
+                            _,
+                            _,
+                            year // trailing comma
+                        ) in cars) {
+                            meanValue += year
+                        }
+                        println(meanValue/cars.size)
+                    }
+                    printMeanValue()
+                }
+                """.trimMargin(),
+            LintError(8, 25, ruleId, "${TRAILING_COMMA.warnText()} after DESTRUCTURING_DECLARATION_ENTRY: year", true),
+            LintError(17, 29, ruleId, "${TRAILING_COMMA.warnText()} after DESTRUCTURING_DECLARATION_ENTRY: year", true),
+            rulesConfigList = getRulesConfig("destructuringDeclaration")
+        )
+    }
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph3/trailing_comma/TrailingCommaExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/trailing_comma/TrailingCommaExpected.kt
new file mode 100644
index 0000000000..82256db930
--- /dev/null
+++ b/diktat-rules/src/test/resources/test/paragraph3/trailing_comma/TrailingCommaExpected.kt
@@ -0,0 +1,73 @@
+package test.paragraph3.trailing_comma
+
+class Customer(
+    val name: String,
+    lastName: String,
+)
+
+fun shift(x: Int,
+          y: Int,
+) {
+    shift(
+        25,
+        20,
+    )
+
+    val colors = listOf(
+        "red",
+        "green",
+        "blue",
+    )
+}
+
+fun getZValue(mySurface: Surface,
+              xValue: Int,
+              yValue: Int,
+) =
+    mySurface[
+            xValue,
+            yValue
+    ]
+
+fun isReferenceApplicable(myReference: KClass<*>,
+) = when (myReference) {
+    Comparable::class,
+    Iterable::class,
+    String::class,
+    -> true
+    else -> false
+}
+
+@ApplicableFor([
+    "serializer",
+    "balancer",
+    "database",
+    "inMemoryCache",
+],
+)
+fun foo() {}
+
+fun <T1,
+        T21,
+        > foo() {}
+
+fun mains() {
+    foo<
+            Comparable<Number,
+                    >,
+            Iterable<Number,
+                    >,
+            >()
+}
+
+fun printMeanValue() {
+    var meanValue: Int = 0
+    for ((
+        _,
+        _,
+        year,
+    ) in cars) {
+        meanValue += year
+    }
+    println(meanValue/cars.size)
+}
\ No newline at end of file
diff --git a/diktat-rules/src/test/resources/test/paragraph3/trailing_comma/TrailingCommaTest.kt b/diktat-rules/src/test/resources/test/paragraph3/trailing_comma/TrailingCommaTest.kt
new file mode 100644
index 0000000000..2aa00d994b
--- /dev/null
+++ b/diktat-rules/src/test/resources/test/paragraph3/trailing_comma/TrailingCommaTest.kt
@@ -0,0 +1,73 @@
+package test.paragraph3.trailing_comma
+
+class Customer(
+    val name: String,
+    lastName: String
+)
+
+fun shift(x: Int,
+          y: Int
+) {
+    shift(
+        25,
+        20
+    )
+
+    val colors = listOf(
+        "red",
+        "green",
+        "blue"
+    )
+}
+
+fun getZValue(mySurface: Surface,
+              xValue: Int,
+              yValue: Int
+) =
+    mySurface[
+            xValue,
+            yValue
+    ]
+
+fun isReferenceApplicable(myReference: KClass<*>
+) = when (myReference) {
+    Comparable::class,
+    Iterable::class,
+    String::class
+    -> true
+    else -> false
+}
+
+@ApplicableFor([
+    "serializer",
+    "balancer",
+    "database",
+    "inMemoryCache"
+]
+)
+fun foo() {}
+
+fun <T1,
+        T21
+        > foo() {}
+
+fun mains() {
+    foo<
+            Comparable<Number
+                    >,
+            Iterable<Number
+                    >
+            >()
+}
+
+fun printMeanValue() {
+    var meanValue: Int = 0
+    for ((
+        _,
+        _,
+        year
+    ) in cars) {
+        meanValue += year
+    }
+    println(meanValue/cars.size)
+}
\ No newline at end of file