Merge pull request #2543 from kbrunham-intel/fix/2542
Fix formatter non-convergence for continuation EOL comments
diff --git a/verible/verilog/formatting/BUILD b/verible/verilog/formatting/BUILD
index 6150594..6431226 100644
--- a/verible/verilog/formatting/BUILD
+++ b/verible/verilog/formatting/BUILD
@@ -163,7 +163,6 @@
"//verible/common/util:expandable-tree-view",
"//verible/common/util:interval",
"//verible/common/util:interval-set",
- "//verible/common/util:iterator-range",
"//verible/common/util:logging",
"//verible/common/util:spacer",
"//verible/common/util:tree-operations",
@@ -175,7 +174,6 @@
"//verible/verilog/analysis:verilog-equivalence",
"//verible/verilog/parser:verilog-token-enum",
"//verible/verilog/preprocessor:verilog-preprocess",
- "@abseil-cpp//absl/base:core_headers",
"@abseil-cpp//absl/log:die_if_null",
"@abseil-cpp//absl/status",
"@abseil-cpp//absl/status:statusor",
diff --git a/verible/verilog/formatting/formatter.cc b/verible/verilog/formatting/formatter.cc
index 9893b60..9bd3dd2 100644
--- a/verible/verilog/formatting/formatter.cc
+++ b/verible/verilog/formatting/formatter.cc
@@ -26,7 +26,6 @@
#include <string_view>
#include <vector>
-#include "absl/base/attributes.h"
#include "absl/log/die_if_null.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
@@ -50,7 +49,6 @@
#include "verible/common/util/expandable-tree-view.h"
#include "verible/common/util/interval-set.h"
#include "verible/common/util/interval.h"
-#include "verible/common/util/iterator-range.h"
#include "verible/common/util/logging.h"
#include "verible/common/util/spacer.h"
#include "verible/common/util/tree-operations.h"
@@ -749,15 +747,24 @@
case verible::SpacingDecision::kPreserve: {
if (token.before.preserved_space_start !=
verible::string_view_null_iterator()) {
- *column += token.OriginalLeadingSpaces().length();
+ const std::string_view leading = token.OriginalLeadingSpaces();
+ const auto last_nl = leading.find_last_of('\n');
+ if (last_nl == std::string_view::npos) {
+ *column += leading.length();
+ } else {
+ // Reset column after the last newline, same as FormattedToken
+ // emit (GitHub issue 2542).
+ *column = static_cast<int>(leading.length() - last_nl - 1);
+ }
} else {
*column += token.before.spaces;
}
break;
}
case verible::SpacingDecision::kWrap:
- *column = 0;
- ABSL_FALLTHROUGH_INTENDED;
+ // Newline then only the wrap indent (same as FormattedToken emit).
+ *column = token.before.spaces;
+ break;
case verible::SpacingDecision::kAlign:
case verible::SpacingDecision::kAppend:
*column += token.before.spaces;
@@ -766,6 +773,15 @@
}
static int CalculateEolCommentColumn(const verible::FormattedExcerpt &line) {
+ // Compute the starting column of the trailing EOL comment the same way
+ // FormattedExcerpt::FormattedText emits spaces, including:
+ // * wrap indents (SpacingDecision::kWrap), and
+ // * preserved leading whitespace that may contain newlines (common when
+ // an original line break is kept). Counting those newlines as width
+ // made continuation comments land on the wrong column and fail to
+ // converge on re-format (GitHub issue 2542).
+ if (line.Tokens().empty()) return 0;
+
int column = 0;
const auto &front = line.Tokens().front();
@@ -777,12 +793,16 @@
}
column += front.token->text().length();
- for (const auto &ftoken : verible::make_range(line.Tokens().begin() + 1,
- line.Tokens().end() - 1)) {
+ const auto &tokens = line.Tokens();
+ for (size_t i = 1; i < tokens.size(); ++i) {
+ const auto &ftoken = tokens[i];
AdjustColumnUsingTokenSpacing(ftoken, &column);
- column += ftoken.token->text().length();
+ // Do not add the last token's length: that is the EOL comment whose
+ // starting column we want.
+ if (i + 1 < tokens.size()) {
+ column += ftoken.token->text().length();
+ }
}
- AdjustColumnUsingTokenSpacing(line.Tokens().back(), &column);
CHECK_GE(column, 0);
return column;
diff --git a/verible/verilog/formatting/formatter_test.cc b/verible/verilog/formatting/formatter_test.cc
index dce9f05..61950a0 100644
--- a/verible/verilog/formatting/formatter_test.cc
+++ b/verible/verilog/formatting/formatter_test.cc
@@ -19380,6 +19380,50 @@
}
}
+// 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).
+TEST(FormatterEndToEndTest, ContinuationCommentAfterWrappedAssignConverges) {
+ static constexpr FormatterTestCase kTestCases[] = {
+ {// Comments originally column-aligned after a wrapped assign
+ "module m;\n"
+ " assign status_ur = !(status_sc || status_ca ||\n"
+ " status_crs); // Completions with a Reserved Completion\n"
+ " // Status value are treated as UR\n"
+ "endmodule\n",
+ "module m;\n"
+ " assign status_ur =\n"
+ " !(status_sc || status_ca || status_crs); // Completions with a "
+ "Reserved Completion\n"
+ " // Status value are "
+ "treated as UR\n"
+ "endmodule\n"},
+ {// Previously mis-aligned continuation is not treated as a continuation
+ // (column delta > 1) and must still converge
+ "module m;\n"
+ " assign status_ur = !(status_sc || status_ca ||\n"
+ " status_crs); // Completions with a Reserved Completion\n"
+ " "
+ "// Status value are treated as UR\n"
+ "endmodule\n",
+ "module m;\n"
+ " assign status_ur =\n"
+ " !(status_sc || status_ca || status_crs); // Completions with a "
+ "Reserved Completion\n"
+ " // Status value are treated as UR\n"
+ "endmodule\n"},
+ };
+ FormatStyle style; // default column_limit (100)
+ for (const auto &test_case : kTestCases) {
+ VLOG(1) << "code-to-format:\n" << test_case.input << "<EOF>";
+ std::ostringstream stream;
+ const auto status =
+ FormatVerilog(test_case.input, "<filename>", style, stream);
+ EXPECT_OK(status) << status.message();
+ EXPECT_EQ(stream.str(), test_case.expected) << "code:\n" << test_case.input;
+ }
+}
+
// Regression for https://github.com/chipsalliance/verible/issues/2540:
// Trailing EOL comment after `end` before `else if` must not change whether
// the else-if assignment stays on one line across re-format (convergence).