You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by rs...@apache.org on 2022/02/07 18:39:17 UTC

[avro] branch master updated: AVRO-3294: Improve IDL doc comment handling (#1453)

This is an automated email from the ASF dual-hosted git repository.

rskraba pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/avro.git


The following commit(s) were added to refs/heads/master by this push:
     new 2681c1b  AVRO-3294: Improve IDL doc comment handling (#1453)
2681c1b is described below

commit 2681c1b0979fc37685d019c0db956593aa2ea5a2
Author: Oscar Westra van Holthe - Kind <op...@users.noreply.github.com>
AuthorDate: Mon Feb 7 19:37:55 2022 +0100

    AVRO-3294: Improve IDL doc comment handling (#1453)
    
    * AVRO-3294: Improve IDL doc comment handling
    
    Use the previously introduced method `DocCommentHandler#clearDoc()` to
    generate warnings for misplaced documentation. Also add documentation to
    describe the tricky bits when using this method.
    
    The brittleness of the solution proves that doc comments as special
    tokens are a hack. However, making regular tokens out of them may break
    existing `.avdl` files.
    
    * AVRO-3294: Trigger build
---
 .../apache/avro/compiler/idl/DocCommentHelper.java |   8 ++
 .../javacc/org/apache/avro/compiler/idl/idl.jj     | 113 ++++++++++++---------
 .../java/compiler/src/test/idl/input/comments.avdl |  37 ++++---
 .../compiler/src/test/idl/output/comments.avpr     |  19 ++--
 .../java/org/apache/avro/compiler/idl/TestIdl.java |  75 +++++++-------
 5 files changed, 140 insertions(+), 112 deletions(-)

diff --git a/lang/java/compiler/src/main/java/org/apache/avro/compiler/idl/DocCommentHelper.java b/lang/java/compiler/src/main/java/org/apache/avro/compiler/idl/DocCommentHelper.java
index 1ccc101..5d0ec52 100644
--- a/lang/java/compiler/src/main/java/org/apache/avro/compiler/idl/DocCommentHelper.java
+++ b/lang/java/compiler/src/main/java/org/apache/avro/compiler/idl/DocCommentHelper.java
@@ -74,6 +74,14 @@ public class DocCommentHelper {
     DOC.set(newDocComment);
   }
 
+  /**
+   * Clear any documentation (and generate a warning if there was).
+   *
+   * This method should NOT be used after an optional component in a grammar
+   * (i.e., after a @code{[…]} or @code{…*} construct), because the optional
+   * grammar part may have already caused parsing a doc comment special token
+   * placed after the code block.
+   */
   static void clearDoc() {
     DocComment oldDocComment = DOC.get();
     if (oldDocComment != null) {
diff --git a/lang/java/compiler/src/main/javacc/org/apache/avro/compiler/idl/idl.jj b/lang/java/compiler/src/main/javacc/org/apache/avro/compiler/idl/idl.jj
index ee467ff..1f931a6 100644
--- a/lang/java/compiler/src/main/javacc/org/apache/avro/compiler/idl/idl.jj
+++ b/lang/java/compiler/src/main/javacc/org/apache/avro/compiler/idl/idl.jj
@@ -1065,7 +1065,7 @@ Protocol CompilationUnit():
 /*
  * Declaration syntax follows.
  */
-private Schema NamedSchemaDeclaration(Map<String, JsonNode> props):
+private Schema NamedSchemaDeclaration(String doc, Map<String, JsonNode> props):
 {
   Schema s;
   String savedSpace = this.namespace;
@@ -1076,9 +1076,9 @@ private Schema NamedSchemaDeclaration(Map<String, JsonNode> props):
       this.namespace = getTextProp("namespace", props, token);
   }
  (
-     s = FixedDeclaration()
-   | s = EnumDeclaration()
-   | s = RecordDeclaration()
+     s = FixedDeclaration(doc)
+   | s = EnumDeclaration(doc)
+   | s = RecordDeclaration(doc)
  )
  {
    this.namespace = savedSpace;
@@ -1125,11 +1125,12 @@ Schema UnionDefinition():
 
 Protocol ProtocolDeclaration():
 {
-  String name;
+  String doc, name;
   Protocol p;
   Map<String, JsonNode> props = new LinkedHashMap<>();
 }
 {
+  doc = Documentation()
   ( SchemaProperty(props) )*
   {
     if (props.containsKey("namespace"))
@@ -1138,7 +1139,7 @@ Protocol ProtocolDeclaration():
  "protocol"
    name = Identifier()
  {
-   p = new Protocol(name, DocCommentHelper.getDoc(), namespace);
+   p = new Protocol(name, doc, namespace);
    for (String key : props.keySet())
      if ("namespace".equals(key)) {               // already handled: ignore
      } else {                                     // add all other properties
@@ -1152,17 +1153,30 @@ Protocol ProtocolDeclaration():
 }
 
 
-Schema EnumDeclaration():
+String Documentation():
+{
+  //noinspection ResultOfMethodCallIgnored
+  getToken(1); // Parse, but don't consume, at least one token; this triggers parsing special tokens like doc comments.
+}
+{
+   // Don't parse anything, just return the doc string
+   {
+       return DocCommentHelper.getDoc();
+   }
+}
+
+
+Schema EnumDeclaration(String doc):
 {
   String name;
   List<String> symbols;
   String defaultSymbol = null;
 }
 {
-  "enum" {   String doc = DocCommentHelper.getDoc(); }
+  "enum"
   name = Identifier()
   symbols = EnumBody()
-      [ <EQUALS> defaultSymbol=Identifier() <SEMICOLON>]
+      [ <EQUALS> defaultSymbol=Identifier() <SEMICOLON> { DocCommentHelper.clearDoc(); } ]
   {
     Schema s = Schema.createEnum(name, doc, namespace, symbols, defaultSymbol);
     names.put(s.getFullName(), s);
@@ -1175,10 +1189,11 @@ List<String> EnumBody():
   List<String> symbols = new ArrayList<>();
 }
 {
-  "{"
+  "{" { DocCommentHelper.clearDoc(); }
   [ EnumConstant(symbols) ( "," EnumConstant(symbols) )* ]
   "}"
   {
+    DocCommentHelper.clearDoc();
     return symbols;
   }
 }
@@ -1193,13 +1208,14 @@ void EnumConstant(List<String> symbols):
 
 void ProtocolBody(Protocol p):
 {
+  String doc;
   Schema schema;
   Message message;
   Protocol importProtocol;
   Map<String, JsonNode> props = new LinkedHashMap<>();
 }
 {
-  "{"
+  "{" { DocCommentHelper.clearDoc(); }
   (
    <IMPORT>
    ((( importProtocol = ImportIdl() | importProtocol = ImportProtocol()) {
@@ -1208,21 +1224,26 @@ void ProtocolBody(Protocol p):
        p.getMessages().putAll(importProtocol.getMessages());
      })
      | schema = ImportSchema()
-     )
+     ) {
+       DocCommentHelper.clearDoc();
+     }
    |
+   doc = Documentation()
    ( SchemaProperty(props) )*
    (
-     schema = NamedSchemaDeclaration(props)
+     schema = NamedSchemaDeclaration(doc, props)
      |
-     message = MessageDeclaration(p, props) {
+     message = MessageDeclaration(p, doc, props) {
        p.getMessages().put(message.getName(), message);
      }
-    )  { props.clear(); }
+    ) {
+      props.clear();
+    }
   ) *
   "}"
-
   {
     p.setTypes(names.values());
+    DocCommentHelper.clearDoc();
   }
 }
 
@@ -1273,7 +1294,7 @@ Schema ImportSchema() : {
     }
 }
 
-Schema FixedDeclaration():
+Schema FixedDeclaration(String doc):
 {
   String name;
   Token sizeTok;
@@ -1282,14 +1303,14 @@ Schema FixedDeclaration():
   "fixed" name = Identifier() "(" sizeTok = <INTEGER_LITERAL> ")"
   ";"
   {
-    Schema s = Schema.createFixed(name, DocCommentHelper.getDoc(), this.namespace,
-                                  Integer.parseInt(sizeTok.image));
+    DocCommentHelper.clearDoc();
+    Schema s = Schema.createFixed(name, doc, this.namespace, Integer.parseInt(sizeTok.image));
     names.put(s.getFullName(), s);
     return s;
   }
 }
 
-Schema RecordDeclaration():
+Schema RecordDeclaration(String doc):
 {
   String name;
   List<Field> fields = new ArrayList<>();
@@ -1302,14 +1323,14 @@ Schema RecordDeclaration():
   )
   name = Identifier()
   {
-    Schema result = Schema.createRecord(
-      name, DocCommentHelper.getDoc(), this.namespace, isError);
+    Schema result = Schema.createRecord(name, doc, this.namespace, isError);
     names.put(result.getFullName(), result);
   }
-  "{"
+  "{" { DocCommentHelper.clearDoc(); }
     ( FieldDeclaration(fields) )*
   "}"
   {
+    DocCommentHelper.clearDoc();
     result.setFields(fields);
     return result;
   }
@@ -1332,27 +1353,27 @@ private void SchemaProperty(Map<String, JsonNode> properties):
 
 void FieldDeclaration(List<Field> fields):
 {
+  String defaultDoc;
   Schema type;
 }
 {
+  defaultDoc = Documentation()
   type = Type()
-  VariableDeclarator(type, fields) ( "," VariableDeclarator(type, fields) )*
-  ";"
+  VariableDeclarator(type, defaultDoc, fields) ( "," VariableDeclarator(type, defaultDoc, fields) )*
+  ";" { DocCommentHelper.clearDoc(); }
 }
 
-void VariableDeclarator(Schema type, List<Field> fields):
+void VariableDeclarator(Schema type, String defaultDoc, List<Field> fields):
 {
-  String name;
+  String doc, name;
   JsonNode defaultValue = null;
   Map<String, JsonNode> props = new LinkedHashMap<>();
 }
 {
-    ( SchemaProperty(props) )*
-
+  doc = Documentation()
+  ( SchemaProperty(props) )*
   name = Identifier()
-
-    [ <EQUALS> defaultValue=Json() ]
-
+  [ <EQUALS> defaultValue=Json() ]
   {
     Field.Order order = Field.Order.ASCENDING;
     for (String key : props.keySet())
@@ -1361,7 +1382,7 @@ void VariableDeclarator(Schema type, List<Field> fields):
 
     boolean validate = SchemaResolver.isFullyResolvedSchema(type);
     Schema fieldType = fixOptionalSchema(type, defaultValue);
-    Field field = Accessor.createField(name, fieldType, DocCommentHelper.getDoc(), defaultValue, validate, order);
+    Field field = Accessor.createField(name, fieldType, doc == null ? defaultDoc : doc, defaultValue, validate, order);
     for (String key : props.keySet())
       if ("order".equals(key)) {                  // already handled: ignore
       } else if ("aliases".equals(key)) {         // aliases
@@ -1371,22 +1392,13 @@ void VariableDeclarator(Schema type, List<Field> fields):
         Accessor.addProp(field, key, props.get(key));
       }
     fields.add(field);
+    DocCommentHelper.clearDoc();
   }
 }
 
 
-String MessageDocumentation():
-{}
-{
-   // Don't parse anything, just return the doc string
-   {
-       return DocCommentHelper.getDoc();
-   }
-}
-
-private Message MessageDeclaration(Protocol p, Map<String, JsonNode> props):
+private Message MessageDeclaration(Protocol p, String msgDoc, Map<String, JsonNode> props):
 {
-  String msgDoc;
   String name;
   Schema request;
   Schema response;
@@ -1395,13 +1407,12 @@ private Message MessageDeclaration(Protocol p, Map<String, JsonNode> props):
   errorSchemata.add(Protocol.SYSTEM_ERROR);
 }
 {
-  msgDoc = MessageDocumentation()
-  response = ResultType()
-  name = Identifier()
+  response = ResultType()  name = Identifier()
   request = FormalParameters()
   [ "oneway" {oneWay = true; } | "throws" ErrorList(errorSchemata) ]
   ";"
   {
+    DocCommentHelper.clearDoc();
     Schema errors = Schema.createUnion(errorSchemata);
     if (oneWay && response.getType() != Type.NULL)
       throw error("One-way message'"+name+"' must return void", token);
@@ -1426,19 +1437,23 @@ Schema FormalParameters():
   List<Field> fields = new ArrayList<>();
 }
 {
-  "(" [ FormalParameter(fields) ( "," FormalParameter(fields) )* ] ")"
+  "(" { DocCommentHelper.clearDoc(); }
+  [ FormalParameter(fields) ( "," FormalParameter(fields) )* ] ")"
   {
+    DocCommentHelper.clearDoc();
     return Schema.createRecord(null, null, null, false, fields);
   }
 }
 
 void FormalParameter(List<Field> fields):
 {
+  String doc;
   Schema type;
 }
 {
+  doc = Documentation()
   type = Type()
-  VariableDeclarator(type, fields)
+  VariableDeclarator(type, doc, fields)
 }
 
 Schema Type():
diff --git a/lang/java/compiler/src/test/idl/input/comments.avdl b/lang/java/compiler/src/test/idl/input/comments.avdl
index 5c61996..38024af 100644
--- a/lang/java/compiler/src/test/idl/input/comments.avdl
+++ b/lang/java/compiler/src/test/idl/input/comments.avdl
@@ -27,41 +27,38 @@ protocol Comments {
     /** Dangling Enum8 */ A
     /** Dangling Enum9 */;
 
-    // The "Dangling Enum9" doc comment above will be attributed to this enum.
-    enum NotUndocumentedEnum {D,E}
+    enum UndocumentedEnum {D,E}
 
-    /** Dangling Fixed1 */ fixed
-    /** Dangling Fixed2 */ DocumentedFixed
-    /** Dangling Fixed3 */(
-    /** Dangling Fixed4 */ 16
-    /** Dangling Fixed5 */)
-    /** Documented Fixed Type */;
+    /** Documented Fixed Type */ fixed
+    /** Dangling Fixed1 */ DocumentedFixed
+    /** Dangling Fixed2 */(
+    /** Dangling Fixed3 */ 16
+    /** Dangling Fixed4 */)
+    /** Dangling Fixed5 */;
 
     fixed UndocumentedFixed(16);
 
-    /** Dangling Error1 */ error
-    /** Documented Error */ DocumentedError
+    /** Documented Error */ error
+    /** Dangling Error1 */ DocumentedError
     /** Dangling Field1 */{
-    /** Dangling Field2 */string
-    /** Dangling Field3 */reason
-    /** Documented Field */;
+    /** Default Doc Explanation Field */string
+    /** Documented Reason Field */reason, explanation
+    /** Dangling Field2 */;
     /** Dangling Error2 */}
 
-    // The "Dangling Error2" doc comment above will be attributed to this record.
-    record NotUndocumentedRecord {
+    record UndocumentedRecord {
         string description;
     }
 
     /** Documented Method */ void
     /** Dangling Param1 */ documentedMethod
     /** Dangling Param2 */(
-        /** Dangling Param3 */ string
-        /** Dangling Param4 */ message
-    /** Documented Parameter */)
+        string /** Documented Parameter */ message,
+        /** Default Documented Parameter */ string defMsg
+    /** Dangling Param3 */)
     /** Dangling Method1 */ throws
     /** Dangling Method2 */ DocumentedError
     /** Dangling Method3 */;
 
-    // The "Dangling Method3" doc comment above will be attributed to this method.
-    void notUndocumentedMethod(string message);
+    void undocumentedMethod(string message);
 }
diff --git a/lang/java/compiler/src/test/idl/output/comments.avpr b/lang/java/compiler/src/test/idl/output/comments.avpr
index 2d98962..9901f8e 100644
--- a/lang/java/compiler/src/test/idl/output/comments.avpr
+++ b/lang/java/compiler/src/test/idl/output/comments.avpr
@@ -9,8 +9,7 @@
     "default" : "A"
   }, {
     "type" : "enum",
-    "name" : "NotUndocumentedEnum",
-    "doc" : "Dangling Enum9",
+    "name" : "UndocumentedEnum",
     "symbols" : [ "D", "E" ]
   }, {
     "type" : "fixed",
@@ -28,12 +27,15 @@
     "fields" : [ {
       "name" : "reason",
       "type" : "string",
-      "doc" : "Documented Field"
+      "doc" : "Documented Reason Field"
+    }, {
+      "name" : "explanation",
+      "type" : "string",
+      "doc" : "Default Doc Explanation Field"
     } ]
   }, {
     "type" : "record",
-    "name" : "NotUndocumentedRecord",
-    "doc" : "Dangling Error2",
+    "name" : "UndocumentedRecord",
     "fields" : [ {
       "name" : "description",
       "type" : "string"
@@ -46,12 +48,15 @@
         "name" : "message",
         "type" : "string",
         "doc" : "Documented Parameter"
+      }, {
+        "name" : "defMsg",
+        "type" : "string",
+        "doc" : "Default Documented Parameter"
       } ],
       "response" : "null",
       "errors" : [ "DocumentedError" ]
     },
-    "notUndocumentedMethod" : {
-      "doc" : "Dangling Method3",
+    "undocumentedMethod" : {
       "request" : [ {
         "name" : "message",
         "type" : "string"
diff --git a/lang/java/compiler/src/test/java/org/apache/avro/compiler/idl/TestIdl.java b/lang/java/compiler/src/test/java/org/apache/avro/compiler/idl/TestIdl.java
index 7e609ba..37e6b2b 100644
--- a/lang/java/compiler/src/test/java/org/apache/avro/compiler/idl/TestIdl.java
+++ b/lang/java/compiler/src/test/java/org/apache/avro/compiler/idl/TestIdl.java
@@ -35,9 +35,11 @@ import java.net.URL;
 import java.net.URLClassLoader;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 
+import static java.util.Objects.requireNonNull;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -46,12 +48,12 @@ import static org.junit.Assert.fail;
 /**
  * Simple test harness for Idl. This relies on an input/ and output/ directory.
  * Inside the input/ directory are .avdl files. Each file should have a
- * corresponding .avpr file in output/. When the test is run, it generates and
+ * corresponding .avpr file in output/. When you run the test, it generates and
  * stringifies each .avdl file and compares it to the expected output, failing
  * if the two differ.
  *
  * To make it simpler to write these tests, you can run ant -Dtestcase=TestIdl
- * -Dtest.idl.mode=write which will *replace* all expected output.
+ * -Dtest.idl.mode=write, which will *replace* all expected output.
  */
 public class TestIdl {
   private static final File TEST_DIR = new File(System.getProperty("test.idl.dir", "src/test/idl"));
@@ -71,7 +73,7 @@ public class TestIdl {
     assertTrue(TEST_OUTPUT_DIR.exists());
 
     tests = new ArrayList<>();
-    for (File inF : TEST_INPUT_DIR.listFiles()) {
+    for (File inF : requireNonNull(TEST_INPUT_DIR.listFiles())) {
       if (!inF.getName().endsWith(".avdl"))
         continue;
       if (inF.getName().startsWith("."))
@@ -100,7 +102,7 @@ public class TestIdl {
     }
 
     if (failed > 0) {
-      fail(String.valueOf(failed) + " tests failed");
+      fail(failed + " tests failed");
     }
   }
 
@@ -121,44 +123,45 @@ public class TestIdl {
       final List<String> warnings = parser.getWarningsAfterParsing();
 
       assertEquals("Documented Enum", protocol.getType("testing.DocumentedEnum").getDoc());
-      assertEquals("Dangling Enum9", protocol.getType("testing.NotUndocumentedEnum").getDoc()); // Arguably a bug
+
       assertEquals("Documented Fixed Type", protocol.getType("testing.DocumentedFixed").getDoc());
-      assertNull(protocol.getType("testing.UndocumentedFixed").getDoc());
+
       final Schema documentedError = protocol.getType("testing.DocumentedError");
       assertEquals("Documented Error", documentedError.getDoc());
-      assertEquals("Documented Field", documentedError.getField("reason").doc());
-      assertEquals("Dangling Error2", protocol.getType("testing.NotUndocumentedRecord").getDoc()); // Arguably a bug
+      assertEquals("Documented Reason Field", documentedError.getField("reason").doc());
+      assertEquals("Default Doc Explanation Field", documentedError.getField("explanation").doc());
+
       final Map<String, Protocol.Message> messages = protocol.getMessages();
-      assertEquals("Documented Method", messages.get("documentedMethod").getDoc());
-      assertEquals("Documented Parameter", messages.get("documentedMethod").getRequest().getField("message").doc());
-      assertEquals("Dangling Method3", messages.get("notUndocumentedMethod").getDoc()); // Arguably a bug
+      final Protocol.Message documentedMethod = messages.get("documentedMethod");
+      assertEquals("Documented Method", documentedMethod.getDoc());
+      assertEquals("Documented Parameter", documentedMethod.getRequest().getField("message").doc());
+      assertEquals("Default Documented Parameter", documentedMethod.getRequest().getField("defMsg").doc());
 
-      assertEquals(23, warnings.size());
-      final String pattern = "Found documentation comment at line %d, column %d. Ignoring previous one at line %d, column %d: \"%s\""
+      assertNull(protocol.getType("testing.UndocumentedEnum").getDoc());
+      assertNull(protocol.getType("testing.UndocumentedFixed").getDoc());
+      assertNull(protocol.getType("testing.UndocumentedRecord").getDoc());
+      assertNull(messages.get("undocumentedMethod").getDoc());
+
+      final String pattern1 = "Found documentation comment at line %d, column %d. Ignoring previous one at line %d, column %d: \"%s\""
+          + "\nDid you mean to use a multiline comment ( /* ... */ ) instead?";
+      final String pattern2 = "Ignoring out-of-place documentation comment at line %d, column %d: \"%s\""
           + "\nDid you mean to use a multiline comment ( /* ... */ ) instead?";
-      assertEquals(String.format(pattern, 21, 47, 21, 10, "Dangling Enum1"), warnings.get(0));
-      assertEquals(String.format(pattern, 22, 9, 21, 47, "Dangling Enum2"), warnings.get(1));
-      assertEquals(String.format(pattern, 23, 9, 22, 9, "Dangling Enum3"), warnings.get(2));
-      assertEquals(String.format(pattern, 24, 9, 23, 9, "Dangling Enum4"), warnings.get(3));
-      assertEquals(String.format(pattern, 25, 5, 24, 9, "Dangling Enum5"), warnings.get(4));
-      assertEquals(String.format(pattern, 26, 5, 25, 5, "Dangling Enum6"), warnings.get(5));
-      assertEquals(String.format(pattern, 27, 5, 26, 5, "Dangling Enum7"), warnings.get(6));
-      assertEquals(String.format(pattern, 28, 5, 27, 5, "Dangling Enum8"), warnings.get(7));
-      assertEquals(String.format(pattern, 34, 5, 33, 5, "Dangling Fixed1"), warnings.get(8));
-      assertEquals(String.format(pattern, 35, 5, 34, 5, "Dangling Fixed2"), warnings.get(9));
-      assertEquals(String.format(pattern, 36, 5, 35, 5, "Dangling Fixed3"), warnings.get(10));
-      assertEquals(String.format(pattern, 37, 5, 36, 5, "Dangling Fixed4"), warnings.get(11));
-      assertEquals(String.format(pattern, 38, 5, 37, 5, "Dangling Fixed5"), warnings.get(12));
-      assertEquals(String.format(pattern, 43, 5, 42, 5, "Dangling Error1"), warnings.get(13));
-      assertEquals(String.format(pattern, 45, 5, 44, 5, "Dangling Field1"), warnings.get(14));
-      assertEquals(String.format(pattern, 46, 5, 45, 5, "Dangling Field2"), warnings.get(15));
-      assertEquals(String.format(pattern, 47, 5, 46, 5, "Dangling Field3"), warnings.get(16));
-      assertEquals(String.format(pattern, 57, 5, 56, 5, "Dangling Param1"), warnings.get(17));
-      assertEquals(String.format(pattern, 58, 9, 57, 5, "Dangling Param2"), warnings.get(18));
-      assertEquals(String.format(pattern, 59, 9, 58, 9, "Dangling Param3"), warnings.get(19));
-      assertEquals(String.format(pattern, 60, 5, 59, 9, "Dangling Param4"), warnings.get(20));
-      assertEquals(String.format(pattern, 62, 5, 61, 5, "Dangling Method1"), warnings.get(21));
-      assertEquals(String.format(pattern, 63, 5, 62, 5, "Dangling Method2"), warnings.get(22));
+      assertEquals(Arrays.asList(String.format(pattern1, 21, 47, 21, 10, "Dangling Enum1"),
+          String.format(pattern2, 21, 47, "Dangling Enum2"), String.format(pattern1, 23, 9, 22, 9, "Dangling Enum3"),
+          String.format(pattern1, 24, 9, 23, 9, "Dangling Enum4"),
+          String.format(pattern1, 25, 5, 24, 9, "Dangling Enum5"), String.format(pattern2, 25, 5, "Dangling Enum6"),
+          String.format(pattern1, 27, 5, 26, 5, "Dangling Enum7"),
+          String.format(pattern1, 28, 5, 27, 5, "Dangling Enum8"), String.format(pattern2, 28, 5, "Dangling Enum9"),
+          String.format(pattern1, 34, 5, 33, 5, "Dangling Fixed1"),
+          String.format(pattern1, 35, 5, 34, 5, "Dangling Fixed2"),
+          String.format(pattern1, 36, 5, 35, 5, "Dangling Fixed3"),
+          String.format(pattern1, 37, 5, 36, 5, "Dangling Fixed4"), String.format(pattern2, 37, 5, "Dangling Fixed5"),
+          String.format(pattern1, 43, 5, 42, 5, "Dangling Error1"), String.format(pattern2, 43, 5, "Dangling Field1"),
+          String.format(pattern2, 46, 5, "Dangling Field2"), String.format(pattern2, 47, 5, "Dangling Error2"),
+          String.format(pattern1, 55, 5, 54, 5, "Dangling Param1"), String.format(pattern2, 55, 5, "Dangling Param2"),
+          String.format(pattern2, 58, 5, "Dangling Param3"), String.format(pattern1, 60, 5, 59, 5, "Dangling Method1"),
+          String.format(pattern1, 61, 5, 60, 5, "Dangling Method2"),
+          String.format(pattern2, 61, 5, "Dangling Method3")), warnings);
     }
   }