formatter: Keep space before # in unqualified typedef A parameterized type in a typedef lost the space before '#': typedef dv_base_env_cov #(.CFG_T(tl_agent_env_cfg)) tl_agent_env_cov; was formatted as: typedef dv_base_env_cov#(.CFG_T(tl_agent_env_cfg)) tl_agent_env_cov; The "no space before #" rule in SpacesRequiredBetween() fires whenever the '#' follows an identifier whose direct parent is kUnqualifiedId, unless the context appears in an exemption list. A typedef was not exempted. Package-qualified types are deliberately left alone, since the existing tests require the "type#(params...)::method(...)" spelling: typedef foo_pkg::baz_t#(.L(L), .W(W)) bar_t; The two cases are distinguished by kQualifiedId, which only wraps the kUnqualifiedId in the qualified form. The exemption is a separate IsInsideFirst() call rather than a fifth entry in the existing list: MatchesTagAnyOf() unrolls at most four tags and aborts at runtime beyond that. Adds formatter tests covering a typedef that already has the space, one that does not, and a short single-parameter case that stays on one line. Fixes #850
diff --git a/verible/verilog/formatting/formatter_test.cc b/verible/verilog/formatting/formatter_test.cc index e885d74..cfd0185 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."};