Skip to content

[clang-format] Properly indent lines inside Verilog case structure #65861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 16, 2023

Conversation

sstwcw
Copy link
Contributor

@sstwcw sstwcw commented Sep 9, 2023

When a statement following a case label had to be broken into multiple lines, the continuation parts were not indented correctly.

Old:

case (data)
  16'd0:
    result = // break here
    10'b0111111111;
endcase

New:

case (data)
  16'd0:
    result = // break here
        10'b0111111111;
endcase

Verilog case labels and the following statements are on the same unwrapped line due to the difficulty of identifying them. So there was a rule in getNewLineColumn to add a level of indentation to the part following the case label. However, in case the line had to be broken again, the code at the end of the function would see that the line was already broken with the continuation part indented, so it would not indent it more. Now State.FirstIndent is changed as well for the part following the case label, so the logic for determining when to add a continuation indentation works.

When a statement following a case label had to be broken into multiple
lines, the continuation parts were not indented correctly.

Old:

```Verilog
case (data)
  16'd0:
    result = // break here
    10'b0111111111;
endcase
```

New:

```Verilog
case (data)
  16'd0:
    result = // break here
        10'b0111111111;
endcase
```

Verilog case labels and the following statements are on the same
unwrapped line due to the difficulty of identifying them.  So there was
a rule in `getNewLineColumn` to add a level of indentation to the part
following the case label.  However, in case the line had to be broken
again, the code at the end of the function would see that the line was
already broken with the continuation part indented, so it would not
indent it more.  Now `State.FirstIndent` is changed as well for the part
following the case label, so the logic for determining when to add a
continuation indentation works.
@sstwcw sstwcw requested a review from a team as a code owner September 9, 2023 20:57
@llvmbot llvmbot added clang Clang issues not falling into any other category clang-format labels Sep 9, 2023
@llvmbot
Copy link
Member

llvmbot commented Sep 9, 2023

@llvm/pr-subscribers-clang

Changes

When a statement following a case label had to be broken into multiple lines, the continuation parts were not indented correctly.

Old:

case (data)
  16'd0:
    result = // break here
    10'b0111111111;
endcase

New:

case (data)
  16'd0:
    result = // break here
        10'b0111111111;
endcase

Verilog case labels and the following statements are on the same unwrapped line due to the difficulty of identifying them. So there was a rule in getNewLineColumn to add a level of indentation to the part following the case label. However, in case the line had to be broken again, the code at the end of the function would see that the line was already broken with the continuation part indented, so it would not indent it more. Now State.FirstIndent is changed as well for the part following the case label, so the logic for determining when to add a continuation indentation works.

Full diff: https://github.com/llvm/llvm-project/pull/65861.diff

2 Files Affected:

  • (modified) clang/lib/Format/ContinuationIndenter.cpp (+16-6)
  • (modified) clang/unittests/Format/FormatTestVerilog.cpp (+47)
diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp
index ac62dab1b07cdca..ec21c181772fa81 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -1204,12 +1204,13 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
                     CurrentState.Indent + Style.ContinuationIndentWidth);
   }
 
-  // After a goto label. Usually labels are on separate lines. However
-  // for Verilog the labels may be only recognized by the annotator and
-  // thus are on the same line as the current token.
-  if ((Style.isVerilog() && Keywords.isVerilogEndOfLabel(Previous)) ||
-      (Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths &&
-       State.Line->First->is(tok::kw_enum))) {
+  // Indentation of the statement following a Verilog case label is taken care
+  // of in moveStateToNextToken.
+  if (Style.isVerilog() && Keywords.isVerilogEndOfLabel(Previous))
+    return State.FirstIndent;
+
+  if (Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths &&
+      State.Line->First->is(tok::kw_enum)) {
     return (Style.IndentWidth * State.Line->First->IndentLevel) +
            Style.IndentWidth;
   }
@@ -1599,6 +1600,15 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
 
   State.Column += Current.ColumnWidth;
   State.NextToken = State.NextToken->Next;
+  // Verilog case labels are are on the same unwrapped lines as the statements
+  // that follow.  TokenAnnotator identifies them and sets MustBreakBefore.
+  // Indentation is taken care of here.  A case label can only have 1 statement
+  // in Verilog, so we don't have to worry about lines that follow.
+  if (Style.isVerilog() && State.NextToken &&
+      State.NextToken->MustBreakBefore &&
+      Keywords.isVerilogEndOfLabel(Current)) {
+    State.FirstIndent += Style.IndentWidth;
+  }
 
   unsigned Penalty =
       handleEndOfLine(Current, State, DryRun, AllowBreak, Newline);
diff --git a/clang/unittests/Format/FormatTestVerilog.cpp b/clang/unittests/Format/FormatTestVerilog.cpp
index 9b090aa74f714d1..7161312126ea6ed 100644
--- a/clang/unittests/Format/FormatTestVerilog.cpp
+++ b/clang/unittests/Format/FormatTestVerilog.cpp
@@ -312,6 +312,53 @@ TEST_F(FormatTestVerilog, Case) {
   verifyFormat("default:\n"
                "  x = '{x: x, default: 9};\n",
                Style);
+  // When the line following the case label needs to be indented, the
+  // continuation should be indented correctly.
+  verifyFormat("case (data)\n"
+               "  16'd0:\n"
+               "    result = //\n"
+               "        10'b0111111111;\n"
+               "endcase");
+  verifyFormat("case (data)\n"
+               "  16'd0, //\n"
+               "      16'd1:\n"
+               "    result = //\n"
+               "        10'b0111111111;\n"
+               "endcase");
+  verifyFormat("case (data)\n"
+               "  16'd0:\n"
+               "    result = (10'b0111111111 + //\n"
+               "              10'b0111111111 + //\n"
+               "              10'b0111111111);\n"
+               "endcase");
+  verifyFormat("case (data)\n"
+               "  16'd0:\n"
+               "    result =              //\n"
+               "        (10'b0111111111 + //\n"
+               "         10'b0111111111 + //\n"
+               "         10'b0111111111);\n"
+               "endcase");
+  verifyFormat("case (data)\n"
+               "  16'd0:\n"
+               "    result =          //\n"
+               "        longfunction( //\n"
+               "            arg);\n"
+               "endcase");
+  Style = getDefaultStyle();
+  Style.ContinuationIndentWidth = 1;
+  verifyFormat("case (data)\n"
+               "  16'd0:\n"
+               "    result = //\n"
+               "     10'b0111111111;\n"
+               "endcase",
+               Style);
+  verifyFormat("case (data)\n"
+               "  16'd0:\n"
+               "    result =       //\n"
+               "     longfunction( //\n"
+               "      arg);\n"
+               "endcase",
+               Style);
 }
 
 TEST_F(FormatTestVerilog, Coverage) {

@llvmbot
Copy link
Member

llvmbot commented Sep 9, 2023

@llvm/pr-subscribers-clang-format

Changes

When a statement following a case label had to be broken into multiple lines, the continuation parts were not indented correctly.

Old:

case (data)
  16'd0:
    result = // break here
    10'b0111111111;
endcase

New:

case (data)
  16'd0:
    result = // break here
        10'b0111111111;
endcase

Verilog case labels and the following statements are on the same unwrapped line due to the difficulty of identifying them. So there was a rule in getNewLineColumn to add a level of indentation to the part following the case label. However, in case the line had to be broken again, the code at the end of the function would see that the line was already broken with the continuation part indented, so it would not indent it more. Now State.FirstIndent is changed as well for the part following the case label, so the logic for determining when to add a continuation indentation works.

Full diff: https://github.com/llvm/llvm-project/pull/65861.diff

2 Files Affected:

  • (modified) clang/lib/Format/ContinuationIndenter.cpp (+16-6)
  • (modified) clang/unittests/Format/FormatTestVerilog.cpp (+47)
diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp
index ac62dab1b07cdca..ec21c181772fa81 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -1204,12 +1204,13 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
                     CurrentState.Indent + Style.ContinuationIndentWidth);
   }
 
-  // After a goto label. Usually labels are on separate lines. However
-  // for Verilog the labels may be only recognized by the annotator and
-  // thus are on the same line as the current token.
-  if ((Style.isVerilog() && Keywords.isVerilogEndOfLabel(Previous)) ||
-      (Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths &&
-       State.Line->First->is(tok::kw_enum))) {
+  // Indentation of the statement following a Verilog case label is taken care
+  // of in moveStateToNextToken.
+  if (Style.isVerilog() && Keywords.isVerilogEndOfLabel(Previous))
+    return State.FirstIndent;
+
+  if (Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths &&
+      State.Line->First->is(tok::kw_enum)) {
     return (Style.IndentWidth * State.Line->First->IndentLevel) +
            Style.IndentWidth;
   }
@@ -1599,6 +1600,15 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
 
   State.Column += Current.ColumnWidth;
   State.NextToken = State.NextToken->Next;
+  // Verilog case labels are are on the same unwrapped lines as the statements
+  // that follow.  TokenAnnotator identifies them and sets MustBreakBefore.
+  // Indentation is taken care of here.  A case label can only have 1 statement
+  // in Verilog, so we don't have to worry about lines that follow.
+  if (Style.isVerilog() && State.NextToken &&
+      State.NextToken->MustBreakBefore &&
+      Keywords.isVerilogEndOfLabel(Current)) {
+    State.FirstIndent += Style.IndentWidth;
+  }
 
   unsigned Penalty =
       handleEndOfLine(Current, State, DryRun, AllowBreak, Newline);
diff --git a/clang/unittests/Format/FormatTestVerilog.cpp b/clang/unittests/Format/FormatTestVerilog.cpp
index 9b090aa74f714d1..7161312126ea6ed 100644
--- a/clang/unittests/Format/FormatTestVerilog.cpp
+++ b/clang/unittests/Format/FormatTestVerilog.cpp
@@ -312,6 +312,53 @@ TEST_F(FormatTestVerilog, Case) {
   verifyFormat("default:\n"
                "  x = '{x: x, default: 9};\n",
                Style);
+  // When the line following the case label needs to be indented, the
+  // continuation should be indented correctly.
+  verifyFormat("case (data)\n"
+               "  16'd0:\n"
+               "    result = //\n"
+               "        10'b0111111111;\n"
+               "endcase");
+  verifyFormat("case (data)\n"
+               "  16'd0, //\n"
+               "      16'd1:\n"
+               "    result = //\n"
+               "        10'b0111111111;\n"
+               "endcase");
+  verifyFormat("case (data)\n"
+               "  16'd0:\n"
+               "    result = (10'b0111111111 + //\n"
+               "              10'b0111111111 + //\n"
+               "              10'b0111111111);\n"
+               "endcase");
+  verifyFormat("case (data)\n"
+               "  16'd0:\n"
+               "    result =              //\n"
+               "        (10'b0111111111 + //\n"
+               "         10'b0111111111 + //\n"
+               "         10'b0111111111);\n"
+               "endcase");
+  verifyFormat("case (data)\n"
+               "  16'd0:\n"
+               "    result =          //\n"
+               "        longfunction( //\n"
+               "            arg);\n"
+               "endcase");
+  Style = getDefaultStyle();
+  Style.ContinuationIndentWidth = 1;
+  verifyFormat("case (data)\n"
+               "  16'd0:\n"
+               "    result = //\n"
+               "     10'b0111111111;\n"
+               "endcase",
+               Style);
+  verifyFormat("case (data)\n"
+               "  16'd0:\n"
+               "    result =       //\n"
+               "     longfunction( //\n"
+               "      arg);\n"
+               "endcase",
+               Style);
 }
 
 TEST_F(FormatTestVerilog, Coverage) {

Comment on lines 1603 to 1605
// Verilog case labels are are on the same unwrapped lines as the statements
// that follow. TokenAnnotator identifies them and sets MustBreakBefore.
// Indentation is taken care of here. A case label can only have 1 statement
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: are are. Also, use single space after ..

@sstwcw sstwcw merged commit 00e794b into llvm:main Sep 16, 2023
@sstwcw sstwcw deleted the format-verilog-case branch September 16, 2023 14:30
ZijunZhaoCCK pushed a commit to ZijunZhaoCCK/llvm-project that referenced this pull request Sep 19, 2023
…lvm#65861)

When a statement following a case label had to be broken into multiple
lines, the continuation parts were not indented correctly.

Old:

```Verilog
case (data)
  16'd0:
    result = // break here
    10'b0111111111;
endcase
```

New:

```Verilog
case (data)
  16'd0:
    result = // break here
        10'b0111111111;
endcase
```

Verilog case labels and the following statements are on the same
unwrapped line due to the difficulty of identifying them. So there was a
rule in `getNewLineColumn` to add a level of indentation to the part
following the case label. However, in case the line had to be broken
again, the code at the end of the function would see that the line was
already broken with the continuation part indented, so it would not
indent it more. Now `State.FirstIndent` is changed as well for the part
following the case label, so the logic for determining when to add a
continuation indentation works.
zahiraam pushed a commit to tahonermann/llvm-project that referenced this pull request Oct 24, 2023
…lvm#65861)

When a statement following a case label had to be broken into multiple
lines, the continuation parts were not indented correctly.

Old:

```Verilog
case (data)
  16'd0:
    result = // break here
    10'b0111111111;
endcase
```

New:

```Verilog
case (data)
  16'd0:
    result = // break here
        10'b0111111111;
endcase
```

Verilog case labels and the following statements are on the same
unwrapped line due to the difficulty of identifying them. So there was a
rule in `getNewLineColumn` to add a level of indentation to the part
following the case label. However, in case the line had to be broken
again, the code at the end of the function would see that the line was
already broken with the continuation part indented, so it would not
indent it more. Now `State.FirstIndent` is changed as well for the part
following the case label, so the logic for determining when to add a
continuation indentation works.
zahiraam pushed a commit to tahonermann/llvm-project that referenced this pull request Oct 24, 2023
…lvm#65861)

When a statement following a case label had to be broken into multiple
lines, the continuation parts were not indented correctly.

Old:

```Verilog
case (data)
  16'd0:
    result = // break here
    10'b0111111111;
endcase
```

New:

```Verilog
case (data)
  16'd0:
    result = // break here
        10'b0111111111;
endcase
```

Verilog case labels and the following statements are on the same
unwrapped line due to the difficulty of identifying them. So there was a
rule in `getNewLineColumn` to add a level of indentation to the part
following the case label. However, in case the line had to be broken
again, the code at the end of the function would see that the line was
already broken with the continuation part indented, so it would not
indent it more. Now `State.FirstIndent` is changed as well for the part
following the case label, so the logic for determining when to add a
continuation indentation works.
@owenca owenca removed the clang Clang issues not falling into any other category label May 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants