Merge pull request #2551 from aishasalim/fix/typedef-space-before-hash

formatter: Keep space before # in unqualified typedef
diff --git a/verible/verilog/formatting/formatter_test.cc b/verible/verilog/formatting/formatter_test.cc
index 247a240..32e3a91 100644
--- a/verible/verilog/formatting/formatter_test.cc
+++ b/verible/verilog/formatting/formatter_test.cc
@@ -4343,6 +4343,19 @@
      "    .L(L),\n"
      "    .W(W)\n"
      ") bar_t;\n"},
+    // unqualified parameterized type keeps a space before '#'
+    {"typedef dv_base_env_cov #(.CFG_T(tl_agent_env_cfg)) tl_agent_env_cov;\n",
+     "typedef dv_base_env_cov #(\n"
+     "    .CFG_T(tl_agent_env_cfg)\n"
+     ") tl_agent_env_cov;\n"},
+    // ... and is inserted when absent
+    {"typedef dv_base_env_cov#(.CFG_T(tl_agent_env_cfg)) tl_agent_env_cov;\n",
+     "typedef dv_base_env_cov #(\n"
+     "    .CFG_T(tl_agent_env_cfg)\n"
+     ") tl_agent_env_cov;\n"},
+    // single short parameter stays on one line
+    {"typedef my_class #(.P(P)) my_class_t;\n",
+     "typedef my_class #(.P(P)) my_class_t;\n"},
 
     // package test cases
     {"package fedex;localparam  int  www=3 ;endpackage   :  fedex\n",
diff --git a/verible/verilog/formatting/token-annotator.cc b/verible/verilog/formatting/token-annotator.cc
index d4af24a..fcbbdbb 100644
--- a/verible/verilog/formatting/token-annotator.cc
+++ b/verible/verilog/formatting/token-annotator.cc
@@ -500,11 +500,23 @@
     // This may be controversial or context-dependent, as parameterized
     // classes often appear with method calls like:
     //   type#(params...)::method(...);
+    // A parameterized type in a typedef keeps the space before '#':
+    //   typedef my_class #(.P(P)) my_class_t;
+    // but a package-qualified type does not, matching the existing
+    // "type#(params...)::method(...)" convention:
+    //   typedef pkg::my_class#(.P(P)) my_class_t;
+    // Kept as separate IsInsideFirst() calls because MatchesTagAnyOf()
+    // only unrolls up to four tags.
+    const bool inside_unqualified_typedef =
+        left_context.IsInsideFirst({NodeEnum::kTypeDeclaration}, {}) &&
+        !left_context.IsInsideFirst({NodeEnum::kQualifiedId}, {});
+
     if (left_context.DirectParentIs(NodeEnum::kUnqualifiedId) &&
         !left_context.IsInsideFirst(
             {NodeEnum::kInstantiationType, NodeEnum::kBindTargetInstance,
              NodeEnum::kExtendsList, NodeEnum::kBraceGroup},
-            {})) {
+            {}) &&
+        !inside_unqualified_typedef) {
       return {0, "No space before # when direct parent is kUnqualifiedId."};
     }
     return {1, "Spaces before # in most other contexts."};