Merge pull request #2606 from kbrunham-intel/fix/2605

Fix timescale CRLF // comments leaving CR in the token
diff --git a/verible/verilog/formatting/formatter_issue_regression_test.cc b/verible/verilog/formatting/formatter_issue_regression_test.cc
index f156371..f27b898 100644
--- a/verible/verilog/formatting/formatter_issue_regression_test.cc
+++ b/verible/verilog/formatting/formatter_issue_regression_test.cc
@@ -109,6 +109,43 @@
   EXPECT_THAT(stream.str(), testing::HasSubstr("`TOKEN_BYTE"));
 }
 
+// Regression for https://github.com/chipsalliance/verible/issues/2605:
+// TIMESCALE_DIRECTIVE's EndOfLineComment handler used yyless(yyleng-1),
+// which left `\r` in the comment token for CRLF files. Emitting that token
+// plus a CRLF terminator produced `\r\r\n` and failed FormatEquivalent.
+TEST(FormatterEndToEndTest, TimescaleCrlfEolComment) {
+  static constexpr FormatterTestCase kTestCases[] = {
+      // Next-line `//` comment after `timescale (the reduced issue case).
+      {"`timescale 1 ps / 1 ps\r\n"
+       "// hello\r\n"
+       "module m;\r\n"
+       "endmodule\r\n",
+       "`timescale 1 ps / 1 ps\r\n"
+       "// hello\r\n"
+       "module m;\r\n"
+       "endmodule\r\n"},
+      // Same-line `//` comment on the `timescale directive.
+      {"`timescale 1 ps / 1 ps // hello\r\n"
+       "module m;\r\n"
+       "endmodule\r\n",
+       "`timescale 1 ps / 1 ps  // hello\r\n"
+       "module m;\r\n"
+       "endmodule\r\n"},
+      // LF control: this path already passed lexical verification.
+      {"`timescale 1 ps / 1 ps\n"
+       "// hello\n"
+       "module m;\n"
+       "endmodule\n",
+       "`timescale 1 ps / 1 ps\n"
+       "// hello\n"
+       "module m;\n"
+       "endmodule\n"},
+  };
+  FormatStyle style;
+  style.line_terminator = verible::LineTerminatorOptionStyle::kAuto;
+  RunFormatterTestCases(style, kTestCases);
+}
+
 // Regression for https://github.com/chipsalliance/verible/issues/2542:
 // Continuation EOL comments after a wrapped assign must keep a stable column
 // across re-format (convergence).
diff --git a/verible/verilog/parser/verilog-lexer_test.cc b/verible/verilog/parser/verilog-lexer_test.cc
index e23b911..7e7c54b 100644
--- a/verible/verilog/parser/verilog-lexer_test.cc
+++ b/verible/verilog/parser/verilog-lexer_test.cc
@@ -2041,6 +2041,24 @@
      {'/', "/"},
      {TK_TimeLiteral, "1ps"},
      {TK_NEWLINE, "\n"}},
+    // Issue #2605: CRLF `//` comments in TIMESCALE_DIRECTIVE must not keep
+    // `\r`.
+    {{DR_timescale, "`timescale"},
+     {TK_SPACE, " "},
+     {TK_TimeLiteral, "1ps"},
+     {'/', "/"},
+     {TK_TimeLiteral, "1ps"},
+     {TK_NEWLINE, "\r\n"},
+     {TK_EOL_COMMENT, "// hello"},
+     {TK_NEWLINE, "\r\n"}},
+    {{DR_timescale, "`timescale"},
+     {TK_SPACE, " "},
+     {TK_TimeLiteral, "1ps"},
+     {'/', "/"},
+     {TK_TimeLiteral, "1ps"},
+     {TK_SPACE, " "},
+     {TK_EOL_COMMENT, "// hello"},
+     {TK_NEWLINE, "\r\n"}},
     // TODO(b/134180314): lexer current drops tokens from pragma sections,
     // but instead emit the tokens and filter them out.
     // {{DR_pragma, "`pragma"}, " fragma", {TK_NEWLINE, "\n"}},
diff --git a/verible/verilog/parser/verilog.lex b/verible/verilog/parser/verilog.lex
index 3afd55e..388d209 100644
--- a/verible/verilog/parser/verilog.lex
+++ b/verible/verilog/parser/verilog.lex
@@ -990,7 +990,13 @@
     return TK_COMMENT_BLOCK;
   }
   {EndOfLineComment} {
-    yyless(yyleng-1);  /* return \n to input stream */
+    // Match IN_EOL_COMMENT: CRLF is a two-character terminator. Using
+    // yyless(yyleng-1) left `\r` in the comment token, so the formatter
+    // emitted `\r\r\n` after `timescale comments (issue #2605, PR #2371).
+    if (yyleng >= 2 && yytext[yyleng - 2] == '\r')
+      yyless(yyleng - 2);  /* return \r\n to input stream */
+    else
+      yyless(yyleng - 1);  /* return \n to input stream */
     UpdateLocation();
     return TK_EOL_COMMENT;
   }