Repository: analysis-dev/diktat (kotlin)

Issue:
Kdoc generation for "throws exception" 
```
    fun readAndParseFile(): TomlFile {
        try {
            val ktomlPath = toml.toPath()
            val ktomlLinesFromFile = FileSystem.SYSTEM.read(ktomlPath) {
                // FixMe: may be we need to read and at the same time parse (to make it parallel)
                generateSequence { readUtf8Line() }.toList()
            }
            return parseStringsToTomlNode(ktomlLinesFromFile)
        } catch (e: FileNotFoundException) {
            println("Not able to find toml-file in the following path: $toml")
            throw e
        }
    }
```

diktat fixes it with `     * @throws e` instead of  `  * @throws  FileNotFoundException`

Pull request description:
### What's done:
* Changed logic
* Added tests

This pull request closes #975

#### Fixme:
* We may also want throwing of local variables with exception type, e.g. `val x = IllegalStateException(); throw x`

Patch:
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocMethods.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocMethods.kt
index 363dfa1ae3..1b96fafc9d 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocMethods.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocMethods.kt
@@ -32,6 +32,7 @@ import org.cqfn.diktat.ruleset.utils.splitPathToDirs
 import com.pinterest.ktlint.core.ast.ElementType
 import com.pinterest.ktlint.core.ast.ElementType.BLOCK
 import com.pinterest.ktlint.core.ast.ElementType.CALL_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.CATCH
 import com.pinterest.ktlint.core.ast.ElementType.COLON
 import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION
 import com.pinterest.ktlint.core.ast.ElementType.EQ
@@ -51,8 +52,12 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl
 import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet
 import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
 import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
+import org.jetbrains.kotlin.psi.KtCallExpression
+import org.jetbrains.kotlin.psi.KtCatchClause
 import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
+import org.jetbrains.kotlin.psi.KtProperty
 import org.jetbrains.kotlin.psi.KtThrowExpression
+import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
 import org.jetbrains.kotlin.psi.psiUtil.referenceExpression
 
 /**
@@ -92,7 +97,7 @@ class KdocMethods(configRules: List<RulesConfig>) : DiktatRule(
 
         val (missingParameters, kDocMissingParameters) = getMissingParameters(node, kdocTags)
 
-        val explicitlyThrownExceptions = getExplicitlyThrownExceptions(node)
+        val explicitlyThrownExceptions = getExplicitlyThrownExceptions(node) + getRethrownExceptions(node)
         val missingExceptions = explicitlyThrownExceptions
             .minus(kdocTags
                 ?.filter { it.knownTag == KDocKnownTag.THROWS }
@@ -154,12 +159,38 @@ class KdocMethods(configRules: List<RulesConfig>) : DiktatRule(
         val codeBlock = node.getFirstChildWithType(BLOCK)
         val throwKeywords = codeBlock?.findAllDescendantsWithSpecificType(THROW)
         return throwKeywords
+            ?.asSequence()
             ?.map { it.psi as KtThrowExpression }
+            ?.filter {
+                // we only take freshly created exceptions here: `throw IAE("stuff")` vs `throw e`
+                it.thrownExpression is KtCallExpression
+            }
             ?.mapNotNull { it.thrownExpression?.referenceExpression()?.text }
             ?.toSet()
             ?: emptySet()
     }
 
+    @Suppress("UnsafeCallOnNullableType")
+    private fun getRethrownExceptions(node: ASTNode) = node.findAllDescendantsWithSpecificType(CATCH).flatMap { catchClauseNode ->
+        // `parameterList` is `@Nullable @IfNotParsed`
+        (catchClauseNode.psi as KtCatchClause).parameterList!!.parameters
+            .filter {
+                // `catch (_: Exception)` - parameter can be anonymous
+                it.name != "_"
+            }
+            .filter { param ->
+                // check whether caught parameter is rethrown in the same catch clause
+                (catchClauseNode.psi as KtCatchClause).catchBody?.collectDescendantsOfType<KtThrowExpression>()?.any {
+                    it.thrownExpression?.referenceExpression()?.text == param.name
+                } == true
+            }
+            .map {
+                // parameter in catch statement `catch (e: Type)` should always have type
+                it.typeReference!!.text
+            }
+    }
+        .toSet()
+
     @Suppress("UnsafeCallOnNullableType")
     private fun handleParamCheck(node: ASTNode,
                                  kdoc: ASTNode?,


Test patch:
diff --git a/diktat-rules/src/test/resources/test/paragraph2/kdoc/package/src/main/kotlin/org/cqfn/diktat/kdoc/methods/ThrowsTagInsertionExpected.kt b/diktat-rules/src/test/resources/test/paragraph2/kdoc/package/src/main/kotlin/org/cqfn/diktat/kdoc/methods/ThrowsTagInsertionExpected.kt
index 6990b971f9..add3a6fa47 100644
--- a/diktat-rules/src/test/resources/test/paragraph2/kdoc/package/src/main/kotlin/org/cqfn/diktat/kdoc/methods/ThrowsTagInsertionExpected.kt
+++ b/diktat-rules/src/test/resources/test/paragraph2/kdoc/package/src/main/kotlin/org/cqfn/diktat/kdoc/methods/ThrowsTagInsertionExpected.kt
@@ -46,4 +46,22 @@ class Example {
         if (true) throw IllegalStateException("Lorem ipsum")
         else throw IllegalAccessException("Dolor sit amet")
     }
+
+    /**
+ * @throws RuntimeException
+ */
+fun test6() {
+        try {
+            foo()
+        } catch (_: NullPointerException) {
+            println("NPE!")
+        } catch (e: RuntimeException) {
+            println("Whoops...")
+            throw e
+        } catch (e: Error) {
+            val x = IllegalStateException()
+            println("Nothing to do here")
+            throw x
+        }
+    }
 }
diff --git a/diktat-rules/src/test/resources/test/paragraph2/kdoc/package/src/main/kotlin/org/cqfn/diktat/kdoc/methods/ThrowsTagInsertionTested.kt b/diktat-rules/src/test/resources/test/paragraph2/kdoc/package/src/main/kotlin/org/cqfn/diktat/kdoc/methods/ThrowsTagInsertionTested.kt
index f710894627..cee11d7491 100644
--- a/diktat-rules/src/test/resources/test/paragraph2/kdoc/package/src/main/kotlin/org/cqfn/diktat/kdoc/methods/ThrowsTagInsertionTested.kt
+++ b/diktat-rules/src/test/resources/test/paragraph2/kdoc/package/src/main/kotlin/org/cqfn/diktat/kdoc/methods/ThrowsTagInsertionTested.kt
@@ -40,4 +40,19 @@ class Example {
         if (true) throw IllegalStateException("Lorem ipsum")
         else throw IllegalAccessException("Dolor sit amet")
     }
+
+    fun test6() {
+        try {
+            foo()
+        } catch (_: NullPointerException) {
+            println("NPE!")
+        } catch (e: RuntimeException) {
+            println("Whoops...")
+            throw e
+        } catch (e: Error) {
+            val x = IllegalStateException()
+            println("Nothing to do here")
+            throw x
+        }
+    }
 }