Repository: checkstyle/checkstyle

Problem statement:
JavadocType: False negative for unknown tag with no description
([Documentation](https://checkstyle.org/checks/javadoc/javadoctype.html))

**config.xml**
```xml
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
        "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
  <module name="TreeWalker">
    <module name="JavadocType"/>
  </module>
</module>
```
<br>

**Test.java**
```java
/**
 * @val
 */
class Test {}

```
<br>


**Current CLI:**
```
java -jar checkstyle-10.14.0-all.jar -c config.xml Test.java
Starting audit...
Audit done.
```
<br>

**Expected:**
```
[ERROR] Test.java:2:4: Unknown tag 'val'. [JavadocType]
```
<hr>

**Explanation:**
- `val` is an unknown tag (allowUnknownTags by default is false), and hence should produce a violation.
- Note that this false negative **_only occurs when_** the tag is not accompanied by any description,  violation is produced as expected when the unknown tag is associated with description.

**Attention note from maintainers:**
https://github.com/checkstyle/checkstyle/blob/72cf172972605f79fa8e7ddeea4623529d30f730/src/main/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocTypeCheck.java#L149-L150
this Check is not AST based, so fix might be not trivial. We can accept fix only if it is simple and does not cause more regressions or false positives. Ideally we should  reimplement this Check to be ast based, example: https://github.com/checkstyle/checkstyle/blob/72cf172972605f79fa8e7ddeea4623529d30f730/src/main/java/com/puppycrawl/tools/checkstyle/checks/javadoc/AtclauseOrderCheck.java#L110


Hints:
Example showing that violation is produced as expected if the unknown tag is with the description. (same config is used)

**Test.java**
```java
/**
 * @val well well well, how the turntables
 */
class Test {}
```

**CLI:** `[ERROR] Test.java:2:4: Unknown tag 'val'. [JavadocType]`

Resolution on https://github.com/checkstyle/checkstyle/issues/14618 is required so that I can decide on how to proceed with this issue.

Patch:
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocTypeCheck.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocTypeCheck.java
--- a/src/main/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocTypeCheck.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocTypeCheck.java
@@ -379,10 +379,9 @@ private List<JavadocTag> getJavadocTags(TextBlock textBlock) {
         final JavadocTags tags = JavadocUtil.getJavadocTags(textBlock,
             JavadocUtil.JavadocTagType.BLOCK);
         if (!allowUnknownTags) {
-            for (final InvalidJavadocTag tag : tags.getInvalidTags()) {
-                log(tag.getLine(), tag.getCol(), MSG_UNKNOWN_TAG,
-                    tag.getName());
-            }
+            tags.getInvalidTags().forEach(tag -> {
+                log(tag.getLine(), tag.getCol(), MSG_UNKNOWN_TAG, tag.getName());
+            });
         }
         return tags.getValidTags();
     }
@@ -481,11 +480,16 @@ private void checkUnusedParamTags(
                         || recordComponentNames.contains(paramName);
 
                 if (!found) {
-                    final String actualParamName =
-                        TYPE_NAME_IN_JAVADOC_TAG_SPLITTER.split(tag.getFirstArg())[0];
-                    log(tag.getLineNo(), tag.getColumnNo(),
-                        MSG_UNUSED_TAG,
-                        JavadocTagInfo.PARAM.getText(), actualParamName);
+                    if (paramName.isEmpty()) {
+                        log(tag.getLineNo(), tag.getColumnNo(), MSG_UNUSED_TAG_GENERAL);
+                    }
+                    else {
+                        final String actualParamName =
+                            TYPE_NAME_IN_JAVADOC_TAG_SPLITTER.split(tag.getFirstArg())[0];
+                        log(tag.getLineNo(), tag.getColumnNo(),
+                            MSG_UNUSED_TAG,
+                            JavadocTagInfo.PARAM.getText(), actualParamName);
+                    }
                 }
             }
         }
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/javadoc/utils/BlockTagUtil.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/javadoc/utils/BlockTagUtil.java
--- a/src/main/java/com/puppycrawl/tools/checkstyle/checks/javadoc/utils/BlockTagUtil.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/javadoc/utils/BlockTagUtil.java
@@ -34,11 +34,11 @@ public final class BlockTagUtil {
 
     /** Block tag pattern for a first line. */
     private static final Pattern BLOCK_TAG_PATTERN_FIRST_LINE = Pattern.compile(
-        "/\\*{2,}\\s*@(\\p{Alpha}+)\\s");
+        "/\\*{2,}\\s*@(\\p{Alpha}+)(\\s|$)");
 
     /** Block tag pattern. */
     private static final Pattern BLOCK_TAG_PATTERN = Pattern.compile(
-        "^\\s*\\**\\s*@(\\p{Alpha}+)\\s");
+        "^\\s*\\**\\s*@(\\p{Alpha}+)(\\s|$)");
 
     /** Closing tag. */
     private static final String JAVADOC_CLOSING_TAG = "*/";


Test patch:
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocTypeCheckTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocTypeCheckTest.java
--- a/src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocTypeCheckTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/JavadocTypeCheckTest.java
@@ -24,6 +24,7 @@
 import static com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTypeCheck.MSG_TAG_FORMAT;
 import static com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTypeCheck.MSG_UNKNOWN_TAG;
 import static com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTypeCheck.MSG_UNUSED_TAG;
+import static com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTypeCheck.MSG_UNUSED_TAG_GENERAL;
 
 import org.junit.jupiter.api.Test;
 
@@ -253,6 +254,7 @@ public void testTypeParameters() throws Exception {
             "59:8: " + getCheckMessage(MSG_UNUSED_TAG, "@param", "<C>"),
             "62:5: " + getCheckMessage(MSG_MISSING_TAG, "@param <B>"),
             "75:5: " + getCheckMessage(MSG_UNUSED_TAG, "@param", "x"),
+            "79:5: " + getCheckMessage(MSG_UNUSED_TAG_GENERAL, "@param"),
         };
         verifyWithInlineConfigParser(
                 getPath("InputJavadocTypeTypeParamsTags_1.java"), expected);
@@ -274,6 +276,7 @@ public void testDontAllowUnusedParameterTag() throws Exception {
         final String[] expected = {
             "20:4: " + getCheckMessage(MSG_UNUSED_TAG, "@param", "BAD"),
             "21:4: " + getCheckMessage(MSG_UNUSED_TAG, "@param", "<BAD>"),
+            "23:4: " + getCheckMessage(MSG_UNUSED_TAG_GENERAL, "@param"),
         };
         verifyWithInlineConfigParser(
                 getPath("InputJavadocTypeUnusedParamInJavadocForClass.java"),
@@ -284,6 +287,8 @@ public void testDontAllowUnusedParameterTag() throws Exception {
     public void testBadTag() throws Exception {
         final String[] expected = {
             "19:4: " + getCheckMessage(MSG_UNKNOWN_TAG, "mytag"),
+            "21:4: " + getCheckMessage(MSG_UNKNOWN_TAG, "mytag"),
+            "28:5: " + getCheckMessage(MSG_UNKNOWN_TAG, "mytag"),
         };
         verifyWithInlineConfigParser(
                 getPath("InputJavadocTypeBadTag.java"),
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/utils/BlockTagUtilTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/utils/BlockTagUtilTest.java
--- a/src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/utils/BlockTagUtilTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/checks/javadoc/utils/BlockTagUtilTest.java
@@ -43,13 +43,14 @@ public void testExtractBlockTags() {
             " * @bar def  ",
             "   @baz ghi  ",
             " * @qux jkl",
+            " * @mytag",
             " */",
         };
 
         final List<TagInfo> tags = BlockTagUtil.extractBlockTags(text);
         assertWithMessage("Invalid tags size")
             .that(tags)
-            .hasSize(4);
+            .hasSize(5);
 
         final TagInfo tag1 = tags.get(0);
         assertTagEquals(tag1, "foo", "abc", 1, 4);
@@ -62,6 +63,25 @@ public void testExtractBlockTags() {
 
         final TagInfo tag4 = tags.get(3);
         assertTagEquals(tag4, "qux", "jkl", 4, 3);
+
+        final TagInfo tag5 = tags.get(4);
+        assertTagEquals(tag5, "mytag", "", 5, 3);
+    }
+
+    @Test
+    public void testExtractBlockTagFirstLine() {
+        final String[] text = {
+            "/** @foo",
+            " */",
+        };
+
+        final List<TagInfo> tags = BlockTagUtil.extractBlockTags(text);
+        assertWithMessage("Invalid tags size")
+            .that(tags)
+            .hasSize(1);
+
+        final TagInfo tag1 = tags.get(0);
+        assertTagEquals(tag1, "foo", "", 1, 4);
     }
 
     @Test
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/javadoc/javadoctype/InputJavadocTypeBadTag.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/javadoc/javadoctype/InputJavadocTypeBadTag.java
--- a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/javadoc/javadoctype/InputJavadocTypeBadTag.java
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/javadoc/javadoctype/InputJavadocTypeBadTag.java
@@ -17,7 +17,14 @@
 /**
  * The following is a bad tag.
  * @mytag Hello   // violation 'Unknown tag 'mytag'.'
+ * // violation below 'Unknown tag 'mytag''
+ * @mytag
  */
 public class InputJavadocTypeBadTag
 {
 }
+
+// violation below 'Unknown tag 'mytag''
+/** @mytag
+ */
+class InputJavadocTypeBadTagFirstLine {}
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/javadoc/javadoctype/InputJavadocTypeTypeParamsTags_1.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/javadoc/javadoctype/InputJavadocTypeTypeParamsTags_1.java
--- a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/javadoc/javadoctype/InputJavadocTypeTypeParamsTags_1.java
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/javadoc/javadoctype/InputJavadocTypeTypeParamsTags_1.java
@@ -74,3 +74,8 @@ public <Z> void unclosedGenericParam()
 
 /** @param x */  // violation 'Unused @param tag for 'x'.'
 class Test_1 {}
+
+// violation below 'Unused Javadoc tag'
+/** @param
+ * */
+class Test_2 {}
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/javadoc/javadoctype/InputJavadocTypeUnusedParamInJavadocForClass.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/javadoc/javadoctype/InputJavadocTypeUnusedParamInJavadocForClass.java
--- a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/javadoc/javadoctype/InputJavadocTypeUnusedParamInJavadocForClass.java
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/javadoc/javadoctype/InputJavadocTypeUnusedParamInJavadocForClass.java
@@ -19,6 +19,7 @@
  *
  * @param BAD This is bad.   // violation 'Unused @param tag for 'BAD'.'
  * @param <BAD> This doesn't exist.   // violation 'Unused @param tag for '<BAD>'.'
+ * // violation below 'Unused Javadoc tag'
  * @param
  */
 public class InputJavadocTypeUnusedParamInJavadocForClass {