Skip to content

Commit c887194

Browse files
committed
[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
1 parent b67ee18 commit c887194

File tree

8 files changed

+239
-11
lines changed

8 files changed

+239
-11
lines changed

clang/lib/Format/ContinuationIndenter.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,8 +1090,12 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
10901090
CurrentState.Indent + Style.ContinuationIndentWidth);
10911091
}
10921092

1093-
if (Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths &&
1094-
State.Line->First->is(tok::kw_enum)) {
1093+
// After a goto label. Usually labels are on separate lines. However
1094+
// for Verilog the labels may be only recognized by the annotator and
1095+
// thus are on the same line as the current token.
1096+
if ((Style.isVerilog() && Keywords.isVerilogEndOfLabel(Previous)) ||
1097+
(Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths &&
1098+
State.Line->First->is(tok::kw_enum))) {
10951099
return (Style.IndentWidth * State.Line->First->IndentLevel) +
10961100
Style.IndentWidth;
10971101
}

clang/lib/Format/Format.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,10 +1347,19 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
13471347
LLVMStyle.WhitespaceSensitiveMacros.push_back("CF_SWIFT_NAME");
13481348

13491349
// Defaults that differ when not C++.
1350-
if (Language == FormatStyle::LK_TableGen)
1350+
switch (Language) {
1351+
case FormatStyle::LK_TableGen:
13511352
LLVMStyle.SpacesInContainerLiterals = false;
1352-
if (LLVMStyle.isJson())
1353+
break;
1354+
case FormatStyle::LK_Json:
13531355
LLVMStyle.ColumnLimit = 0;
1356+
break;
1357+
case FormatStyle::LK_Verilog:
1358+
LLVMStyle.IndentCaseLabels = true;
1359+
break;
1360+
default:
1361+
break;
1362+
}
13541363

13551364
return LLVMStyle;
13561365
}

clang/lib/Format/FormatToken.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ namespace format {
3939
TYPE(CastRParen) \
4040
TYPE(ClassLBrace) \
4141
TYPE(CompoundRequirementLBrace) \
42+
/* ternary ?: expression */ \
4243
TYPE(ConditionalExpr) \
44+
/* the condition in an if statement */ \
45+
TYPE(ConditionLParen) \
4346
TYPE(ConflictAlternative) \
4447
TYPE(ConflictEnd) \
4548
TYPE(ConflictStart) \
@@ -67,6 +70,9 @@ namespace format {
6770
TYPE(FunctionLBrace) \
6871
TYPE(FunctionLikeOrFreestandingMacro) \
6972
TYPE(FunctionTypeLParen) \
73+
/* The colon at the end of a goto label or a case label. Currently only used \
74+
* for Verilog. */ \
75+
TYPE(GotoLabelColon) \
7076
TYPE(IfMacro) \
7177
TYPE(ImplicitStringLiteral) \
7278
TYPE(InheritanceColon) \
@@ -1765,6 +1771,15 @@ struct AdditionalKeywords {
17651771
kw_task);
17661772
}
17671773

1774+
bool isVerilogEndOfLabel(const FormatToken &Tok) const {
1775+
const FormatToken *Next = Tok.getNextNonComment();
1776+
// In Verilog the colon in a default label is optional.
1777+
return Tok.is(TT_GotoLabelColon) ||
1778+
(Tok.is(tok::kw_default) &&
1779+
!(Next && Next->isOneOf(tok::colon, tok::semi, kw_clocking, kw_iff,
1780+
kw_input, kw_output, kw_sequence)));
1781+
}
1782+
17681783
/// Whether the token begins a block.
17691784
bool isBlockBegin(const FormatToken &Tok, const FormatStyle &Style) const {
17701785
return Tok.is(TT_MacroBlockBegin) ||

clang/lib/Format/TokenAnnotator.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -955,11 +955,22 @@ class AnnotatingParser {
955955
break;
956956
}
957957
} else if (Style.isVerilog() && Tok->isNot(TT_BinaryOperator)) {
958+
// The distribution weight operators are labeled
959+
// TT_BinaryOperator by the lexer.
958960
if (Keywords.isVerilogEnd(*Tok->Previous) ||
959961
Keywords.isVerilogBegin(*Tok->Previous)) {
960962
Tok->setType(TT_VerilogBlockLabelColon);
961963
} else if (Contexts.back().ContextKind == tok::l_square) {
962964
Tok->setType(TT_BitFieldColon);
965+
} else if (Contexts.back().ColonIsDictLiteral) {
966+
Tok->setType(TT_DictLiteral);
967+
} else if (Contexts.size() == 1) {
968+
// In Verilog a case label doesn't have the case keyword. We
969+
// assume a colon following an expression is a case label.
970+
// Colons from ?: are annotated in parseConditional().
971+
Tok->setType(TT_GotoLabelColon);
972+
if (Line.Level > 1 || (!Line.InPPDirective && Line.Level > 0))
973+
--Line.Level;
963974
}
964975
break;
965976
}
@@ -1230,6 +1241,13 @@ class AnnotatingParser {
12301241
if (Contexts.back().ContextType == Context::ForEachMacro)
12311242
Contexts.back().IsExpression = true;
12321243
break;
1244+
case tok::kw_default:
1245+
// Unindent case labels.
1246+
if (Style.isVerilog() && Keywords.isVerilogEndOfLabel(*Tok) &&
1247+
(Line.Level > 1 || (!Line.InPPDirective && Line.Level > 0))) {
1248+
--Line.Level;
1249+
}
1250+
break;
12331251
case tok::identifier:
12341252
if (Tok->isOneOf(Keywords.kw___has_include,
12351253
Keywords.kw___has_include_next)) {
@@ -2609,6 +2627,10 @@ class ExpressionParser {
26092627
Keywords.kw_throws)) {
26102628
return 0;
26112629
}
2630+
// In Verilog case labels are not on separate lines straight out of
2631+
// UnwrappedLineParser. The colon is not part of an expression.
2632+
if (Style.isVerilog() && Current->is(tok::colon))
2633+
return 0;
26122634
}
26132635
return -1;
26142636
}
@@ -3614,7 +3636,8 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
36143636
return true;
36153637
if (Left.isOneOf(tok::pp_elif, tok::kw_for, tok::kw_while, tok::kw_switch,
36163638
tok::kw_case, TT_ForEachMacro, TT_ObjCForIn) ||
3617-
Left.isIf(Line.Type != LT_PreprocessorDirective)) {
3639+
Left.isIf(Line.Type != LT_PreprocessorDirective) ||
3640+
Right.is(TT_ConditionLParen)) {
36183641
return Style.SpaceBeforeParensOptions.AfterControlStatements ||
36193642
spaceRequiredBeforeParens(Right);
36203643
}
@@ -4085,6 +4108,11 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
40854108
Style.BitFieldColonSpacing == FormatStyle::BFCS_After;
40864109
}
40874110
if (Right.is(tok::colon)) {
4111+
if (Right.is(TT_GotoLabelColon) ||
4112+
(!Style.isVerilog() &&
4113+
Line.First->isOneOf(tok::kw_default, tok::kw_case))) {
4114+
return Style.SpaceBeforeCaseColon;
4115+
}
40884116
if (Line.First->isOneOf(tok::kw_default, tok::kw_case))
40894117
return Style.SpaceBeforeCaseColon;
40904118
const FormatToken *Next = Right.getNextNonComment();
@@ -4347,6 +4375,11 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
43474375
Right.Next->is(tok::string_literal)) {
43484376
return true;
43494377
}
4378+
} else if (Style.isVerilog()) {
4379+
// Break after labels. In Verilog labels don't have the 'case' keyword, so
4380+
// it is hard to identify them in UnwrappedLineParser.
4381+
if (!Keywords.isVerilogBegin(Right) && Keywords.isVerilogEndOfLabel(Left))
4382+
return true;
43504383
} else if (Style.Language == FormatStyle::LK_Cpp ||
43514384
Style.Language == FormatStyle::LK_ObjC ||
43524385
Style.Language == FormatStyle::LK_Proto ||

clang/lib/Format/UnwrappedLineParser.cpp

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -576,9 +576,12 @@ bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace,
576576
LLVM_FALLTHROUGH;
577577
}
578578
case tok::kw_case:
579-
if (Style.isJavaScript() && Line->MustBeDeclaration) {
580-
// A 'case: string' style field declaration.
581-
parseStructuralElement();
579+
if (Style.isVerilog() ||
580+
(Style.isJavaScript() && Line->MustBeDeclaration)) {
581+
// Verilog: Case labels don't have this word. We handle case
582+
// labels including default in TokenAnnotator.
583+
// JavaScript: A 'case: string' style field declaration.
584+
ParseDefault();
582585
break;
583586
}
584587
if (!SwitchLabelEncountered &&
@@ -1534,6 +1537,9 @@ void UnwrappedLineParser::parseStructuralElement(
15341537
parseSwitch();
15351538
return;
15361539
case tok::kw_default:
1540+
// In Verilog default along with other labels are handled in the next loop.
1541+
if (Style.isVerilog())
1542+
break;
15371543
if (Style.isJavaScript() && Line->MustBeDeclaration) {
15381544
// 'default: string' field declaration.
15391545
break;
@@ -1546,6 +1552,12 @@ void UnwrappedLineParser::parseStructuralElement(
15461552
// e.g. "default void f() {}" in a Java interface.
15471553
break;
15481554
case tok::kw_case:
1555+
// In Verilog switch is called case.
1556+
if (Style.isVerilog()) {
1557+
parseBlock();
1558+
addUnwrappedLine();
1559+
return;
1560+
}
15491561
if (Style.isJavaScript() && Line->MustBeDeclaration) {
15501562
// 'case: string' field declaration.
15511563
nextToken();
@@ -1956,7 +1968,9 @@ void UnwrappedLineParser::parseStructuralElement(
19561968
return I != E && (++I == E);
19571969
};
19581970
if (OneTokenSoFar()) {
1959-
if (FormatTok->is(tok::colon) && !Line->MustBeDeclaration) {
1971+
// In Verilog labels can be any expression, so we don't do them here.
1972+
if (!Style.isVerilog() && FormatTok->is(tok::colon) &&
1973+
!Line->MustBeDeclaration) {
19601974
Line->Tokens.begin()->Tok->MustBreakBefore = true;
19611975
parseLabel(!Style.IndentGotoLabels);
19621976
if (HasLabel)
@@ -2013,13 +2027,43 @@ void UnwrappedLineParser::parseStructuralElement(
20132027
parseNew();
20142028
break;
20152029
case tok::kw_case:
2030+
// In Verilog switch is called case.
2031+
if (Style.isVerilog()) {
2032+
parseBlock();
2033+
addUnwrappedLine();
2034+
return;
2035+
}
20162036
if (Style.isJavaScript() && Line->MustBeDeclaration) {
20172037
// 'case: string' field declaration.
20182038
nextToken();
20192039
break;
20202040
}
20212041
parseCaseLabel();
20222042
break;
2043+
case tok::kw_default:
2044+
nextToken();
2045+
if (Style.isVerilog()) {
2046+
if (FormatTok->is(tok::colon)) {
2047+
// The label will be handled in the next iteration.
2048+
break;
2049+
}
2050+
if (FormatTok->is(Keywords.kw_clocking)) {
2051+
// A default clocking block.
2052+
parseBlock();
2053+
addUnwrappedLine();
2054+
return;
2055+
}
2056+
parseVerilogCaseLabel();
2057+
return;
2058+
}
2059+
break;
2060+
case tok::colon:
2061+
nextToken();
2062+
if (Style.isVerilog()) {
2063+
parseVerilogCaseLabel();
2064+
return;
2065+
}
2066+
break;
20232067
default:
20242068
nextToken();
20252069
break;
@@ -4075,10 +4119,13 @@ unsigned UnwrappedLineParser::parseVerilogHierarchyHeader() {
40754119
} else if (FormatTok->isOneOf(tok::kw_case, Keywords.kw_casex,
40764120
Keywords.kw_casez, Keywords.kw_randcase,
40774121
Keywords.kw_randsequence)) {
4078-
AddLevels += Style.IndentCaseLabels;
4122+
if (Style.IndentCaseLabels)
4123+
AddLevels++;
40794124
nextToken();
4080-
if (FormatTok->is(tok::l_paren))
4125+
if (FormatTok->is(tok::l_paren)) {
4126+
FormatTok->setFinalizedType(TT_ConditionLParen);
40814127
parseParens();
4128+
}
40824129
if (FormatTok->isOneOf(Keywords.kw_inside, Keywords.kw_matches))
40834130
nextToken();
40844131
// The case header has no semicolon.
@@ -4176,6 +4223,26 @@ void UnwrappedLineParser::parseVerilogTable() {
41764223
addUnwrappedLine();
41774224
}
41784225

4226+
void UnwrappedLineParser::parseVerilogCaseLabel() {
4227+
// The label will get unindented in AnnotatingParser. If there are no leading
4228+
// spaces, indent the rest here so that things inside the block will be
4229+
// indented relative to things outside. We don't use parseLabel because we
4230+
// don't know whether this colon is a label or a ternary expression at this
4231+
// point.
4232+
auto OrigLevel = Line->Level;
4233+
auto FirstLine = CurrentLines->size();
4234+
if (Line->Level == 0 || (Line->InPPDirective && Line->Level <= 1))
4235+
++Line->Level;
4236+
else if (!Style.IndentCaseBlocks && Keywords.isVerilogBegin(*FormatTok))
4237+
--Line->Level;
4238+
parseStructuralElement();
4239+
// Restore the indentation in both the new line and the line that has the
4240+
// label.
4241+
if (CurrentLines->size() > FirstLine)
4242+
(*CurrentLines)[FirstLine].Level = OrigLevel;
4243+
Line->Level = OrigLevel;
4244+
}
4245+
41794246
LLVM_ATTRIBUTE_UNUSED static void printDebugInfo(const UnwrappedLine &Line,
41804247
StringRef Prefix = "") {
41814248
llvm::dbgs() << Prefix << "Line(" << Line.Level

clang/lib/Format/UnwrappedLineParser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ class UnwrappedLineParser {
184184
// level for a block, used for indenting case labels.
185185
unsigned parseVerilogHierarchyHeader();
186186
void parseVerilogTable();
187+
void parseVerilogCaseLabel();
187188

188189
// Used by addUnwrappedLine to denote whether to keep or remove a level
189190
// when resetting the line state.

clang/unittests/Format/FormatTestVerilog.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,90 @@ TEST_F(FormatTestVerilog, Block) {
116116
"x = x;");
117117
}
118118

119+
TEST_F(FormatTestVerilog, Case) {
120+
verifyFormat("case (data)\n"
121+
"endcase");
122+
verifyFormat("casex (data)\n"
123+
"endcase");
124+
verifyFormat("casez (data)\n"
125+
"endcase");
126+
verifyFormat("case (data) inside\n"
127+
"endcase");
128+
verifyFormat("case (data)\n"
129+
" 16'd0:\n"
130+
" result = 10'b0111111111;\n"
131+
"endcase");
132+
verifyFormat("case (data)\n"
133+
" xxxxxxxx:\n"
134+
" result = 10'b0111111111;\n"
135+
"endcase");
136+
// Test labels with multiple options.
137+
verifyFormat("case (data)\n"
138+
" 16'd0, 16'd1:\n"
139+
" result = 10'b0111111111;\n"
140+
"endcase");
141+
verifyFormat("case (data)\n"
142+
" 16'd0, //\n"
143+
" 16'd1:\n"
144+
" result = 10'b0111111111;\n"
145+
"endcase");
146+
// Test that blocks following labels are indented.
147+
verifyFormat("case (data)\n"
148+
" 16'd1: fork\n"
149+
" result = 10'b1011111111;\n"
150+
" join\n"
151+
"endcase\n");
152+
verifyFormat("case (data)\n"
153+
" 16'd1: fork : x\n"
154+
" result = 10'b1011111111;\n"
155+
" join : x\n"
156+
"endcase\n");
157+
// Test default.
158+
verifyFormat("case (data)\n"
159+
" default\n"
160+
" result = 10'b1011111111;\n"
161+
"endcase");
162+
verifyFormat("case (data)\n"
163+
" default:\n"
164+
" result = 10'b1011111111;\n"
165+
"endcase");
166+
// Test that question marks and colons don't get mistaken as labels.
167+
verifyFormat("case (data)\n"
168+
" 8'b1???????:\n"
169+
" instruction1(ir);\n"
170+
"endcase");
171+
verifyFormat("case (data)\n"
172+
" x ? 8'b1??????? : 1:\n"
173+
" instruction3(ir);\n"
174+
"endcase");
175+
// Test indention options.
176+
auto Style = getLLVMStyle(FormatStyle::LK_Verilog);
177+
Style.IndentCaseLabels = false;
178+
verifyFormat("case (data)\n"
179+
"16'd0:\n"
180+
" result = 10'b0111111111;\n"
181+
"endcase",
182+
Style);
183+
verifyFormat("case (data)\n"
184+
"16'd0: begin\n"
185+
" result = 10'b0111111111;\n"
186+
"end\n"
187+
"endcase",
188+
Style);
189+
Style.IndentCaseLabels = true;
190+
verifyFormat("case (data)\n"
191+
" 16'd0:\n"
192+
" result = 10'b0111111111;\n"
193+
"endcase",
194+
Style);
195+
verifyFormat("case (data)\n"
196+
" 16'd0: begin\n"
197+
" result = 10'b0111111111;\n"
198+
" end\n"
199+
"endcase",
200+
Style);
201+
}
202+
119203
TEST_F(FormatTestVerilog, Delay) {
120204
// Delay by the default unit.
121205
verifyFormat("#0;");

0 commit comments

Comments
 (0)