[clang] c887194 - [clang-format] Handle Verilog case statements

via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 28 17:39:09 PDT 2022


Author: sstwcw
Date: 2022-07-29T00:38:30Z
New Revision: c88719483c69f7f34be4487f6623b06c37475e5a

URL: https://github.com/llvm/llvm-project/commit/c88719483c69f7f34be4487f6623b06c37475e5a
DIFF: https://github.com/llvm/llvm-project/commit/c88719483c69f7f34be4487f6623b06c37475e5a.diff

LOG: [clang-format] Handle Verilog case statements

These statements are like switch statements in C, but without the 'case'
keyword in labels.

How labels are parsed.  In UnwrappedLineParser, the program tries to
parse a statement every time it sees a colon.  In TokenAnnotator, a
colon that isn't part of an expression is annotated as a label.

The token type `TT_GotoLabelColon` is added.  We did not include Verilog
in the name because we thought we would eventually have to fix the
problem that case labels in C can't contain ternary conditional
expressions and we would use that token type.

The style is like below.  Labels are on separate lines and indented by
default.  The linked style guide also has examples where labels and the
corresponding statements are on the same lines.  They are not supported
for now.

https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md

```
case (state_q)
  StIdle:
    state_d = StA;
  StA: begin
    state_d = StB;
  end
endcase
```

Differential Revision: https://reviews.llvm.org/D128714

Added: 
    

Modified: 
    clang/lib/Format/ContinuationIndenter.cpp
    clang/lib/Format/Format.cpp
    clang/lib/Format/FormatToken.h
    clang/lib/Format/TokenAnnotator.cpp
    clang/lib/Format/UnwrappedLineParser.cpp
    clang/lib/Format/UnwrappedLineParser.h
    clang/unittests/Format/FormatTestVerilog.cpp
    clang/unittests/Format/TokenAnnotatorTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp
index 651ec80d6196..e4610064ff7d 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -1090,8 +1090,12 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
                     CurrentState.Indent + Style.ContinuationIndentWidth);
   }
 
-  if (Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths &&
-      State.Line->First->is(tok::kw_enum)) {
+  // 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))) {
     return (Style.IndentWidth * State.Line->First->IndentLevel) +
            Style.IndentWidth;
   }

diff  --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 2659fa2af1a7..57d796c7087f 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1347,10 +1347,19 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
   LLVMStyle.WhitespaceSensitiveMacros.push_back("CF_SWIFT_NAME");
 
   // Defaults that 
diff er when not C++.
-  if (Language == FormatStyle::LK_TableGen)
+  switch (Language) {
+  case FormatStyle::LK_TableGen:
     LLVMStyle.SpacesInContainerLiterals = false;
-  if (LLVMStyle.isJson())
+    break;
+  case FormatStyle::LK_Json:
     LLVMStyle.ColumnLimit = 0;
+    break;
+  case FormatStyle::LK_Verilog:
+    LLVMStyle.IndentCaseLabels = true;
+    break;
+  default:
+    break;
+  }
 
   return LLVMStyle;
 }

diff  --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index f95a27630a79..2d63cbc28aa4 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -39,7 +39,10 @@ namespace format {
   TYPE(CastRParen)                                                             \
   TYPE(ClassLBrace)                                                            \
   TYPE(CompoundRequirementLBrace)                                              \
+  /* ternary ?: expression */                                                  \
   TYPE(ConditionalExpr)                                                        \
+  /* the condition in an if statement */                                       \
+  TYPE(ConditionLParen)                                                        \
   TYPE(ConflictAlternative)                                                    \
   TYPE(ConflictEnd)                                                            \
   TYPE(ConflictStart)                                                          \
@@ -67,6 +70,9 @@ namespace format {
   TYPE(FunctionLBrace)                                                         \
   TYPE(FunctionLikeOrFreestandingMacro)                                        \
   TYPE(FunctionTypeLParen)                                                     \
+  /* The colon at the end of a goto label or a case label. Currently only used \
+   * for Verilog. */                                                           \
+  TYPE(GotoLabelColon)                                                         \
   TYPE(IfMacro)                                                                \
   TYPE(ImplicitStringLiteral)                                                  \
   TYPE(InheritanceColon)                                                       \
@@ -1765,6 +1771,15 @@ struct AdditionalKeywords {
                        kw_task);
   }
 
+  bool isVerilogEndOfLabel(const FormatToken &Tok) const {
+    const FormatToken *Next = Tok.getNextNonComment();
+    // In Verilog the colon in a default label is optional.
+    return Tok.is(TT_GotoLabelColon) ||
+           (Tok.is(tok::kw_default) &&
+            !(Next && Next->isOneOf(tok::colon, tok::semi, kw_clocking, kw_iff,
+                                    kw_input, kw_output, kw_sequence)));
+  }
+
   /// Whether the token begins a block.
   bool isBlockBegin(const FormatToken &Tok, const FormatStyle &Style) const {
     return Tok.is(TT_MacroBlockBegin) ||

diff  --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 3dd550256469..67ee194e38ea 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -955,11 +955,22 @@ class AnnotatingParser {
           break;
         }
       } else if (Style.isVerilog() && Tok->isNot(TT_BinaryOperator)) {
+        // The distribution weight operators are labeled
+        // TT_BinaryOperator by the lexer.
         if (Keywords.isVerilogEnd(*Tok->Previous) ||
             Keywords.isVerilogBegin(*Tok->Previous)) {
           Tok->setType(TT_VerilogBlockLabelColon);
         } else if (Contexts.back().ContextKind == tok::l_square) {
           Tok->setType(TT_BitFieldColon);
+        } else if (Contexts.back().ColonIsDictLiteral) {
+          Tok->setType(TT_DictLiteral);
+        } else if (Contexts.size() == 1) {
+          // In Verilog a case label doesn't have the case keyword. We
+          // assume a colon following an expression is a case label.
+          // Colons from ?: are annotated in parseConditional().
+          Tok->setType(TT_GotoLabelColon);
+          if (Line.Level > 1 || (!Line.InPPDirective && Line.Level > 0))
+            --Line.Level;
         }
         break;
       }
@@ -1230,6 +1241,13 @@ class AnnotatingParser {
       if (Contexts.back().ContextType == Context::ForEachMacro)
         Contexts.back().IsExpression = true;
       break;
+    case tok::kw_default:
+      // Unindent case labels.
+      if (Style.isVerilog() && Keywords.isVerilogEndOfLabel(*Tok) &&
+          (Line.Level > 1 || (!Line.InPPDirective && Line.Level > 0))) {
+        --Line.Level;
+      }
+      break;
     case tok::identifier:
       if (Tok->isOneOf(Keywords.kw___has_include,
                        Keywords.kw___has_include_next)) {
@@ -2609,6 +2627,10 @@ class ExpressionParser {
                            Keywords.kw_throws)) {
         return 0;
       }
+      // In Verilog case labels are not on separate lines straight out of
+      // UnwrappedLineParser. The colon is not part of an expression.
+      if (Style.isVerilog() && Current->is(tok::colon))
+        return 0;
     }
     return -1;
   }
@@ -3614,7 +3636,8 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
       return true;
     if (Left.isOneOf(tok::pp_elif, tok::kw_for, tok::kw_while, tok::kw_switch,
                      tok::kw_case, TT_ForEachMacro, TT_ObjCForIn) ||
-        Left.isIf(Line.Type != LT_PreprocessorDirective)) {
+        Left.isIf(Line.Type != LT_PreprocessorDirective) ||
+        Right.is(TT_ConditionLParen)) {
       return Style.SpaceBeforeParensOptions.AfterControlStatements ||
              spaceRequiredBeforeParens(Right);
     }
@@ -4085,6 +4108,11 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
            Style.BitFieldColonSpacing == FormatStyle::BFCS_After;
   }
   if (Right.is(tok::colon)) {
+    if (Right.is(TT_GotoLabelColon) ||
+        (!Style.isVerilog() &&
+         Line.First->isOneOf(tok::kw_default, tok::kw_case))) {
+      return Style.SpaceBeforeCaseColon;
+    }
     if (Line.First->isOneOf(tok::kw_default, tok::kw_case))
       return Style.SpaceBeforeCaseColon;
     const FormatToken *Next = Right.getNextNonComment();
@@ -4347,6 +4375,11 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
         Right.Next->is(tok::string_literal)) {
       return true;
     }
+  } else if (Style.isVerilog()) {
+    // Break after labels. In Verilog labels don't have the 'case' keyword, so
+    // it is hard to identify them in UnwrappedLineParser.
+    if (!Keywords.isVerilogBegin(Right) && Keywords.isVerilogEndOfLabel(Left))
+      return true;
   } else if (Style.Language == FormatStyle::LK_Cpp ||
              Style.Language == FormatStyle::LK_ObjC ||
              Style.Language == FormatStyle::LK_Proto ||

diff  --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 719532b28ffc..8336c4c727aa 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -576,9 +576,12 @@ bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace,
       LLVM_FALLTHROUGH;
     }
     case tok::kw_case:
-      if (Style.isJavaScript() && Line->MustBeDeclaration) {
-        // A 'case: string' style field declaration.
-        parseStructuralElement();
+      if (Style.isVerilog() ||
+          (Style.isJavaScript() && Line->MustBeDeclaration)) {
+        // Verilog: Case labels don't have this word. We handle case
+        // labels including default in TokenAnnotator.
+        // JavaScript: A 'case: string' style field declaration.
+        ParseDefault();
         break;
       }
       if (!SwitchLabelEncountered &&
@@ -1534,6 +1537,9 @@ void UnwrappedLineParser::parseStructuralElement(
     parseSwitch();
     return;
   case tok::kw_default:
+    // In Verilog default along with other labels are handled in the next loop.
+    if (Style.isVerilog())
+      break;
     if (Style.isJavaScript() && Line->MustBeDeclaration) {
       // 'default: string' field declaration.
       break;
@@ -1546,6 +1552,12 @@ void UnwrappedLineParser::parseStructuralElement(
     // e.g. "default void f() {}" in a Java interface.
     break;
   case tok::kw_case:
+    // In Verilog switch is called case.
+    if (Style.isVerilog()) {
+      parseBlock();
+      addUnwrappedLine();
+      return;
+    }
     if (Style.isJavaScript() && Line->MustBeDeclaration) {
       // 'case: string' field declaration.
       nextToken();
@@ -1956,7 +1968,9 @@ void UnwrappedLineParser::parseStructuralElement(
         return I != E && (++I == E);
       };
       if (OneTokenSoFar()) {
-        if (FormatTok->is(tok::colon) && !Line->MustBeDeclaration) {
+        // In Verilog labels can be any expression, so we don't do them here.
+        if (!Style.isVerilog() && FormatTok->is(tok::colon) &&
+            !Line->MustBeDeclaration) {
           Line->Tokens.begin()->Tok->MustBreakBefore = true;
           parseLabel(!Style.IndentGotoLabels);
           if (HasLabel)
@@ -2013,6 +2027,12 @@ void UnwrappedLineParser::parseStructuralElement(
       parseNew();
       break;
     case tok::kw_case:
+      // In Verilog switch is called case.
+      if (Style.isVerilog()) {
+        parseBlock();
+        addUnwrappedLine();
+        return;
+      }
       if (Style.isJavaScript() && Line->MustBeDeclaration) {
         // 'case: string' field declaration.
         nextToken();
@@ -2020,6 +2040,30 @@ void UnwrappedLineParser::parseStructuralElement(
       }
       parseCaseLabel();
       break;
+    case tok::kw_default:
+      nextToken();
+      if (Style.isVerilog()) {
+        if (FormatTok->is(tok::colon)) {
+          // The label will be handled in the next iteration.
+          break;
+        }
+        if (FormatTok->is(Keywords.kw_clocking)) {
+          // A default clocking block.
+          parseBlock();
+          addUnwrappedLine();
+          return;
+        }
+        parseVerilogCaseLabel();
+        return;
+      }
+      break;
+    case tok::colon:
+      nextToken();
+      if (Style.isVerilog()) {
+        parseVerilogCaseLabel();
+        return;
+      }
+      break;
     default:
       nextToken();
       break;
@@ -4075,10 +4119,13 @@ unsigned UnwrappedLineParser::parseVerilogHierarchyHeader() {
   } else if (FormatTok->isOneOf(tok::kw_case, Keywords.kw_casex,
                                 Keywords.kw_casez, Keywords.kw_randcase,
                                 Keywords.kw_randsequence)) {
-    AddLevels += Style.IndentCaseLabels;
+    if (Style.IndentCaseLabels)
+      AddLevels++;
     nextToken();
-    if (FormatTok->is(tok::l_paren))
+    if (FormatTok->is(tok::l_paren)) {
+      FormatTok->setFinalizedType(TT_ConditionLParen);
       parseParens();
+    }
     if (FormatTok->isOneOf(Keywords.kw_inside, Keywords.kw_matches))
       nextToken();
     // The case header has no semicolon.
@@ -4176,6 +4223,26 @@ void UnwrappedLineParser::parseVerilogTable() {
   addUnwrappedLine();
 }
 
+void UnwrappedLineParser::parseVerilogCaseLabel() {
+  // The label will get unindented in AnnotatingParser. If there are no leading
+  // spaces, indent the rest here so that things inside the block will be
+  // indented relative to things outside. We don't use parseLabel because we
+  // don't know whether this colon is a label or a ternary expression at this
+  // point.
+  auto OrigLevel = Line->Level;
+  auto FirstLine = CurrentLines->size();
+  if (Line->Level == 0 || (Line->InPPDirective && Line->Level <= 1))
+    ++Line->Level;
+  else if (!Style.IndentCaseBlocks && Keywords.isVerilogBegin(*FormatTok))
+    --Line->Level;
+  parseStructuralElement();
+  // Restore the indentation in both the new line and the line that has the
+  // label.
+  if (CurrentLines->size() > FirstLine)
+    (*CurrentLines)[FirstLine].Level = OrigLevel;
+  Line->Level = OrigLevel;
+}
+
 LLVM_ATTRIBUTE_UNUSED static void printDebugInfo(const UnwrappedLine &Line,
                                                  StringRef Prefix = "") {
   llvm::dbgs() << Prefix << "Line(" << Line.Level

diff  --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h
index 15c279dbe3df..f05d715f4efc 100644
--- a/clang/lib/Format/UnwrappedLineParser.h
+++ b/clang/lib/Format/UnwrappedLineParser.h
@@ -184,6 +184,7 @@ class UnwrappedLineParser {
   // level for a block, used for indenting case labels.
   unsigned parseVerilogHierarchyHeader();
   void parseVerilogTable();
+  void parseVerilogCaseLabel();
 
   // Used by addUnwrappedLine to denote whether to keep or remove a level
   // when resetting the line state.

diff  --git a/clang/unittests/Format/FormatTestVerilog.cpp b/clang/unittests/Format/FormatTestVerilog.cpp
index 3460065e3efb..28980c39dcf3 100644
--- a/clang/unittests/Format/FormatTestVerilog.cpp
+++ b/clang/unittests/Format/FormatTestVerilog.cpp
@@ -116,6 +116,90 @@ TEST_F(FormatTestVerilog, Block) {
                "x = x;");
 }
 
+TEST_F(FormatTestVerilog, Case) {
+  verifyFormat("case (data)\n"
+               "endcase");
+  verifyFormat("casex (data)\n"
+               "endcase");
+  verifyFormat("casez (data)\n"
+               "endcase");
+  verifyFormat("case (data) inside\n"
+               "endcase");
+  verifyFormat("case (data)\n"
+               "  16'd0:\n"
+               "    result = 10'b0111111111;\n"
+               "endcase");
+  verifyFormat("case (data)\n"
+               "  xxxxxxxx:\n"
+               "    result = 10'b0111111111;\n"
+               "endcase");
+  // Test labels with multiple options.
+  verifyFormat("case (data)\n"
+               "  16'd0, 16'd1:\n"
+               "    result = 10'b0111111111;\n"
+               "endcase");
+  verifyFormat("case (data)\n"
+               "  16'd0, //\n"
+               "      16'd1:\n"
+               "    result = 10'b0111111111;\n"
+               "endcase");
+  // Test that blocks following labels are indented.
+  verifyFormat("case (data)\n"
+               "  16'd1: fork\n"
+               "    result = 10'b1011111111;\n"
+               "  join\n"
+               "endcase\n");
+  verifyFormat("case (data)\n"
+               "  16'd1: fork : x\n"
+               "    result = 10'b1011111111;\n"
+               "  join : x\n"
+               "endcase\n");
+  // Test default.
+  verifyFormat("case (data)\n"
+               "  default\n"
+               "    result = 10'b1011111111;\n"
+               "endcase");
+  verifyFormat("case (data)\n"
+               "  default:\n"
+               "    result = 10'b1011111111;\n"
+               "endcase");
+  // Test that question marks and colons don't get mistaken as labels.
+  verifyFormat("case (data)\n"
+               "  8'b1???????:\n"
+               "    instruction1(ir);\n"
+               "endcase");
+  verifyFormat("case (data)\n"
+               "  x ? 8'b1??????? : 1:\n"
+               "    instruction3(ir);\n"
+               "endcase");
+  // Test indention options.
+  auto Style = getLLVMStyle(FormatStyle::LK_Verilog);
+  Style.IndentCaseLabels = false;
+  verifyFormat("case (data)\n"
+               "16'd0:\n"
+               "  result = 10'b0111111111;\n"
+               "endcase",
+               Style);
+  verifyFormat("case (data)\n"
+               "16'd0: begin\n"
+               "  result = 10'b0111111111;\n"
+               "end\n"
+               "endcase",
+               Style);
+  Style.IndentCaseLabels = true;
+  verifyFormat("case (data)\n"
+               "  16'd0:\n"
+               "    result = 10'b0111111111;\n"
+               "endcase",
+               Style);
+  verifyFormat("case (data)\n"
+               "  16'd0: begin\n"
+               "    result = 10'b0111111111;\n"
+               "  end\n"
+               "endcase",
+               Style);
+}
+
 TEST_F(FormatTestVerilog, Delay) {
   // Delay by the default unit.
   verifyFormat("#0;");

diff  --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp
index 9fe89eead213..9c3606f49a41 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -838,6 +838,21 @@ TEST_F(TokenAnnotatorTest, UnderstandsVerilogOperators) {
   Tokens = Annotate("extern function [1 : 0] x;");
   ASSERT_EQ(Tokens.size(), 10u) << Tokens;
   EXPECT_TOKEN(Tokens[4], tok::colon, TT_BitFieldColon);
+  // Test case labels and ternary operators.
+  Tokens = Annotate("case (x)\n"
+                    "  x:\n"
+                    "    x;\n"
+                    "endcase\n");
+  ASSERT_EQ(Tokens.size(), 10u) << Tokens;
+  EXPECT_TOKEN(Tokens[5], tok::colon, TT_GotoLabelColon);
+  Tokens = Annotate("case (x)\n"
+                    "  x ? x : x:\n"
+                    "    x;\n"
+                    "endcase\n");
+  ASSERT_EQ(Tokens.size(), 14u) << Tokens;
+  EXPECT_TOKEN(Tokens[5], tok::question, TT_ConditionalExpr);
+  EXPECT_TOKEN(Tokens[7], tok::colon, TT_ConditionalExpr);
+  EXPECT_TOKEN(Tokens[9], tok::colon, TT_GotoLabelColon);
 }
 
 } // namespace


        


More information about the cfe-commits mailing list