This revision was automatically updated to reflect the committed changes.
Closed by commit rGc88719483c69: [clang-format] Handle Verilog case statements
(authored by sstwcw).
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D128714/new/
https://reviews.llvm.org/D128714
Files:
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
Index: clang/unittests/Format/TokenAnnotatorTest.cpp
===================================================================
--- clang/unittests/Format/TokenAnnotatorTest.cpp
+++ clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -838,6 +838,21 @@
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
Index: clang/unittests/Format/FormatTestVerilog.cpp
===================================================================
--- clang/unittests/Format/FormatTestVerilog.cpp
+++ clang/unittests/Format/FormatTestVerilog.cpp
@@ -116,6 +116,90 @@
"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;");
Index: clang/lib/Format/UnwrappedLineParser.h
===================================================================
--- clang/lib/Format/UnwrappedLineParser.h
+++ clang/lib/Format/UnwrappedLineParser.h
@@ -184,6 +184,7 @@
// 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.
Index: clang/lib/Format/UnwrappedLineParser.cpp
===================================================================
--- clang/lib/Format/UnwrappedLineParser.cpp
+++ clang/lib/Format/UnwrappedLineParser.cpp
@@ -576,9 +576,12 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
}
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 @@
} 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 @@
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
Index: clang/lib/Format/TokenAnnotator.cpp
===================================================================
--- clang/lib/Format/TokenAnnotator.cpp
+++ clang/lib/Format/TokenAnnotator.cpp
@@ -955,11 +955,22 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 ||
Index: clang/lib/Format/FormatToken.h
===================================================================
--- clang/lib/Format/FormatToken.h
+++ clang/lib/Format/FormatToken.h
@@ -39,7 +39,10 @@
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 @@
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 @@
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) ||
Index: clang/lib/Format/Format.cpp
===================================================================
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -1347,10 +1347,19 @@
LLVMStyle.WhitespaceSensitiveMacros.push_back("CF_SWIFT_NAME");
// Defaults that differ 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;
}
Index: clang/lib/Format/ContinuationIndenter.cpp
===================================================================
--- clang/lib/Format/ContinuationIndenter.cpp
+++ clang/lib/Format/ContinuationIndenter.cpp
@@ -1090,8 +1090,12 @@
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;
}
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits