[clang] 6db0c18 - [clang-format] Handle Verilog modules
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 28 17:39:06 PDT 2022
Author: sstwcw
Date: 2022-07-29T00:38:30Z
New Revision: 6db0c18b1af653f33dd4629d6155f6cf334a975e
URL: https://github.com/llvm/llvm-project/commit/6db0c18b1af653f33dd4629d6155f6cf334a975e
DIFF: https://github.com/llvm/llvm-project/commit/6db0c18b1af653f33dd4629d6155f6cf334a975e.diff
LOG: [clang-format] Handle Verilog modules
Now things inside hierarchies like modules and interfaces are
indented. When the module header spans multiple lines, all except the
first line are indented as continuations. We added the property
`IsContinuation` to mark lines that should be indented this way.
In order that the colons inside square brackets don't get labeled as
`TT_ObjCMethodExpr`, we added a check to only use this type when the
language is not Verilog.
Differential Revision: https://reviews.llvm.org/D128712
Added:
Modified:
clang/lib/Format/FormatToken.h
clang/lib/Format/TokenAnnotator.cpp
clang/lib/Format/TokenAnnotator.h
clang/lib/Format/UnwrappedLineFormatter.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/FormatToken.h b/clang/lib/Format/FormatToken.h
index abd386d98b83..90dc1f010a8e 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -137,6 +137,10 @@ namespace format {
TYPE(UntouchableMacroFunc) \
/* like in begin : block */ \
TYPE(VerilogBlockLabelColon) \
+ /* The square bracket for the dimension part of the type name. \
+ * In 'logic [1:0] x[1:0]', only the first '['. This way we can have space \
+ * before the first bracket but not the second. */ \
+ TYPE(VerilogDimensionedTypeName) \
/* for the base in a number literal, not including the quote */ \
TYPE(VerilogNumberBase) \
TYPE(Unknown)
@@ -1743,6 +1747,22 @@ struct AdditionalKeywords {
kw_join_any, kw_join_none);
}
+ /// Returns whether \p Tok is a Verilog keyword that opens a module, etc.
+ bool isVerilogHierarchy(const FormatToken &Tok) const {
+ if (Tok.endsSequence(kw_function, kw_with))
+ return false;
+ if (Tok.is(kw_property)) {
+ const FormatToken *Prev = Tok.getPreviousNonComment();
+ return !(Prev &&
+ Prev->isOneOf(tok::kw_restrict, kw_assert, kw_assume, kw_cover));
+ }
+ return Tok.isOneOf(tok::kw_case, tok::kw_class, kw_function, kw_module,
+ kw_interface, kw_package, kw_casex, kw_casez, kw_checker,
+ kw_clocking, kw_covergroup, kw_macromodule, kw_primitive,
+ kw_program, kw_property, kw_randcase, kw_randsequence,
+ kw_task);
+ }
+
/// 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 560a1a30ca8c..4f5ca9fed3aa 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -238,11 +238,13 @@ class AnnotatingParser {
}
bool StartsObjCMethodExpr = false;
- if (FormatToken *MaybeSel = OpeningParen.Previous) {
- // @selector( starts a selector.
- if (MaybeSel->isObjCAtKeyword(tok::objc_selector) && MaybeSel->Previous &&
- MaybeSel->Previous->is(tok::at)) {
- StartsObjCMethodExpr = true;
+ if (!Style.isVerilog()) {
+ if (FormatToken *MaybeSel = OpeningParen.Previous) {
+ // @selector( starts a selector.
+ if (MaybeSel->isObjCAtKeyword(tok::objc_selector) &&
+ MaybeSel->Previous && MaybeSel->Previous->is(tok::at)) {
+ StartsObjCMethodExpr = true;
+ }
}
}
@@ -761,7 +763,8 @@ class AnnotatingParser {
// Remember that this is a [[using ns: foo]] C++ attribute, so we
// don't add a space before the colon (unlike other colons).
CurrentToken->setType(TT_AttributeColon);
- } else if (Left->isOneOf(TT_ArraySubscriptLSquare,
+ } else if (!Style.isVerilog() &&
+ Left->isOneOf(TT_ArraySubscriptLSquare,
TT_DesignatedInitializerLSquare)) {
Left->setType(TT_ObjCMethodExpr);
StartsObjCMethodExpr = true;
@@ -951,6 +954,8 @@ class AnnotatingParser {
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);
}
break;
}
@@ -1230,6 +1235,8 @@ class AnnotatingParser {
Tok->Next->isNot(tok::l_paren)) {
Tok->setType(TT_CSharpGenericTypeConstraint);
parseCSharpGenericTypeConstraint();
+ if (Tok->getPreviousNonComment() == nullptr)
+ Line.IsContinuation = true;
}
break;
case tok::arrow:
@@ -1411,8 +1418,8 @@ class AnnotatingParser {
IdentifierInfo *Info = CurrentToken->Tok.getIdentifierInfo();
if ((Style.Language == FormatStyle::LK_Java &&
CurrentToken->is(Keywords.kw_package)) ||
- (Info && Info->getPPKeywordID() == tok::pp_import &&
- CurrentToken->Next &&
+ (!Style.isVerilog() && Info &&
+ Info->getPPKeywordID() == tok::pp_import && CurrentToken->Next &&
CurrentToken->Next->isOneOf(tok::string_literal, tok::identifier,
tok::kw_static))) {
next();
@@ -4009,6 +4016,11 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
(Left.is(TT_VerilogNumberBase) && Right.is(tok::numeric_constant))) {
return false;
}
+ // Add space between the type name and dimension like `logic [1:0]`.
+ if (Right.is(tok::l_square) &&
+ Left.isOneOf(TT_VerilogDimensionedTypeName, Keywords.kw_function)) {
+ return true;
+ }
// Don't add spaces between a casting type and the quote or repetition count
// and the brace.
if ((Right.is(Keywords.kw_apostrophe) ||
@@ -4990,7 +5002,8 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
}
void TokenAnnotator::printDebugInfo(const AnnotatedLine &Line) const {
- llvm::errs() << "AnnotatedTokens(L=" << Line.Level << "):\n";
+ llvm::errs() << "AnnotatedTokens(L=" << Line.Level << ", T=" << Line.Type
+ << ", C=" << Line.IsContinuation << "):\n";
const FormatToken *Tok = Line.First;
while (Tok) {
llvm::errs() << " M=" << Tok->MustBreakBefore
diff --git a/clang/lib/Format/TokenAnnotator.h b/clang/lib/Format/TokenAnnotator.h
index 1be64ed6d3b5..0e485ee9f0b4 100644
--- a/clang/lib/Format/TokenAnnotator.h
+++ b/clang/lib/Format/TokenAnnotator.h
@@ -43,6 +43,7 @@ class AnnotatedLine {
MustBeDeclaration(Line.MustBeDeclaration), MightBeFunctionDecl(false),
IsMultiVariableDeclStmt(false), Affected(false),
LeadingEmptyLinesAffected(false), ChildrenAffected(false),
+ IsContinuation(Line.IsContinuation),
FirstStartColumn(Line.FirstStartColumn) {
assert(!Line.Tokens.empty());
@@ -143,6 +144,10 @@ class AnnotatedLine {
/// \c True if one of this line's children intersects with an input range.
bool ChildrenAffected;
+ /// \c True if this line should be indented by ContinuationIndent in addition
+ /// to the normal indention level.
+ bool IsContinuation;
+
unsigned FirstStartColumn;
private:
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index abeb93d23776..9fb02eb8acb7 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -69,7 +69,7 @@ class LevelIndentTracker {
}
if (static_cast<int>(Indent) + Offset >= 0)
Indent += Offset;
- if (Line.First->is(TT_CSharpGenericTypeConstraint))
+ if (Line.IsContinuation)
Indent = Line.Level * Style.IndentWidth + Style.ContinuationIndentWidth;
}
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index d0bfc68fdd6d..3253e5703077 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -839,8 +839,13 @@ FormatToken *UnwrappedLineParser::parseBlock(
}
};
+ // Whether this is a Verilog-specific block that has a special header like a
+ // module.
+ const bool VerilogHierarchy =
+ Style.isVerilog() && Keywords.isVerilogHierarchy(*FormatTok);
assert((FormatTok->isOneOf(tok::l_brace, TT_MacroBlockBegin) ||
- (Style.isVerilog() && Keywords.isVerilogBegin(*FormatTok))) &&
+ (Style.isVerilog() &&
+ (Keywords.isVerilogBegin(*FormatTok) || VerilogHierarchy))) &&
"'{' or macro block token expected");
FormatToken *Tok = FormatTok;
const bool FollowedByComment = Tokens->peekNextToken()->is(tok::comment);
@@ -850,14 +855,20 @@ FormatToken *UnwrappedLineParser::parseBlock(
// For Whitesmiths mode, jump to the next level prior to skipping over the
// braces.
- if (AddLevels > 0 && Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths)
+ if (!VerilogHierarchy && AddLevels > 0 &&
+ Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths) {
++Line->Level;
+ }
size_t PPStartHash = computePPHash();
const unsigned InitialLevel = Line->Level;
- nextToken(/*LevelDifference=*/AddLevels);
- HandleVerilogBlockLabel();
+ if (VerilogHierarchy) {
+ AddLevels += parseVerilogHierarchyHeader();
+ } else {
+ nextToken(/*LevelDifference=*/AddLevels);
+ HandleVerilogBlockLabel();
+ }
// Bail out if there are too many levels. Otherwise, the stack might overflow.
if (Line->Level > 300)
@@ -1552,7 +1563,14 @@ void UnwrappedLineParser::parseStructuralElement(
return;
case tok::kw_extern:
nextToken();
- if (FormatTok->is(tok::string_literal)) {
+ if (Style.isVerilog()) {
+ // In Verilog and extern module declaration looks like a start of module.
+ // But there is no body and endmodule. So we handle it separately.
+ if (Keywords.isVerilogHierarchy(*FormatTok)) {
+ parseVerilogHierarchyHeader();
+ return;
+ }
+ } else if (FormatTok->is(tok::string_literal)) {
nextToken();
if (FormatTok->is(tok::l_brace)) {
if (Style.BraceWrapping.AfterExternBlock)
@@ -1751,9 +1769,15 @@ void UnwrappedLineParser::parseStructuralElement(
parseEnum();
}
break;
+ case tok::kw_class:
+ if (Style.isVerilog()) {
+ parseBlock();
+ addUnwrappedLine();
+ return;
+ }
+ LLVM_FALLTHROUGH;
case tok::kw_struct:
case tok::kw_union:
- case tok::kw_class:
if (parseStructLike())
return;
break;
@@ -1889,7 +1913,8 @@ void UnwrappedLineParser::parseStructuralElement(
}
if (Style.isVerilog()) {
- if (Keywords.isVerilogBegin(*FormatTok)) {
+ if (Keywords.isVerilogBegin(*FormatTok) ||
+ Keywords.isVerilogHierarchy(*FormatTok)) {
parseBlock();
addUnwrappedLine();
return;
@@ -3998,6 +4023,137 @@ void UnwrappedLineParser::parseStatementMacro() {
addUnwrappedLine();
}
+void UnwrappedLineParser::parseVerilogHierarchyIdentifier() {
+ // consume things like a::`b.c[d:e] or a::*
+ while (true) {
+ if (FormatTok->isOneOf(tok::star, tok::period, tok::periodstar,
+ tok::coloncolon, tok::hash) ||
+ Keywords.isVerilogIdentifier(*FormatTok)) {
+ nextToken();
+ } else if (FormatTok->is(tok::l_square)) {
+ parseSquare();
+ } else {
+ break;
+ }
+ }
+}
+
+void UnwrappedLineParser::parseVerilogSensitivityList() {
+ assert(FormatTok->is(tok::at));
+ nextToken();
+ // A block event expression has 2 at signs.
+ if (FormatTok->is(tok::at))
+ nextToken();
+ switch (FormatTok->Tok.getKind()) {
+ case tok::star:
+ nextToken();
+ break;
+ case tok::l_paren:
+ parseParens();
+ break;
+ default:
+ parseVerilogHierarchyIdentifier();
+ break;
+ }
+}
+
+unsigned UnwrappedLineParser::parseVerilogHierarchyHeader() {
+ unsigned AddLevels = 0;
+
+ if (FormatTok->is(Keywords.kw_clocking)) {
+ nextToken();
+ if (Keywords.isVerilogIdentifier(*FormatTok))
+ nextToken();
+ if (FormatTok->is(tok::at))
+ parseVerilogSensitivityList();
+ if (FormatTok->is(tok::semi))
+ nextToken();
+ } else if (FormatTok->isOneOf(tok::kw_case, Keywords.kw_casex,
+ Keywords.kw_casez, Keywords.kw_randcase,
+ Keywords.kw_randsequence)) {
+ if (Style.IndentCaseLabels)
+ ++AddLevels;
+ nextToken();
+ if (FormatTok->is(tok::l_paren))
+ parseParens();
+ if (FormatTok->isOneOf(Keywords.kw_inside, Keywords.kw_matches))
+ nextToken();
+ // The case header has no semicolon.
+ } else {
+ // "module" etc.
+ nextToken();
+ // all the words like the name of the module and specifiers like
+ // "automatic" and the width of function return type
+ while (true) {
+ if (FormatTok->is(tok::l_square)) {
+ auto Prev = FormatTok->getPreviousNonComment();
+ if (Prev && Keywords.isVerilogIdentifier(*Prev))
+ Prev->setFinalizedType(TT_VerilogDimensionedTypeName);
+ parseSquare();
+ } else if (Keywords.isVerilogIdentifier(*FormatTok) ||
+ FormatTok->isOneOf(Keywords.kw_automatic, tok::kw_static)) {
+ nextToken();
+ } else {
+ break;
+ }
+ }
+
+ auto NewLine = [this]() {
+ addUnwrappedLine();
+ Line->IsContinuation = true;
+ };
+
+ // package imports
+ while (FormatTok->is(Keywords.kw_import)) {
+ NewLine();
+ nextToken();
+ parseVerilogHierarchyIdentifier();
+ if (FormatTok->is(tok::semi))
+ nextToken();
+ }
+
+ // parameters and ports
+ if (FormatTok->is(Keywords.kw_verilogHash)) {
+ NewLine();
+ nextToken();
+ if (FormatTok->is(tok::l_paren))
+ parseParens();
+ }
+ if (FormatTok->is(tok::l_paren)) {
+ NewLine();
+ parseParens();
+ }
+
+ // extends and implements
+ if (FormatTok->is(Keywords.kw_extends)) {
+ NewLine();
+ nextToken();
+ parseVerilogHierarchyIdentifier();
+ if (FormatTok->is(tok::l_paren))
+ parseParens();
+ }
+ if (FormatTok->is(Keywords.kw_implements)) {
+ NewLine();
+ do {
+ nextToken();
+ parseVerilogHierarchyIdentifier();
+ } while (FormatTok->is(tok::comma));
+ }
+
+ // Coverage event for cover groups.
+ if (FormatTok->is(tok::at)) {
+ NewLine();
+ parseVerilogSensitivityList();
+ }
+
+ if (FormatTok->is(tok::semi))
+ nextToken(/*LevelDifference=*/1);
+ addUnwrappedLine();
+ }
+
+ return AddLevels;
+}
+
LLVM_ATTRIBUTE_UNUSED static void printDebugInfo(const UnwrappedLine &Line,
StringRef Prefix = "") {
llvm::dbgs() << Prefix << "Line(" << Line.Level
@@ -4035,6 +4191,7 @@ void UnwrappedLineParser::addUnwrappedLine(LineLevel AdjustLevel) {
Line->Tokens.clear();
Line->MatchingOpeningBlockLineIndex = UnwrappedLine::kInvalidIndex;
Line->FirstStartColumn = 0;
+ Line->IsContinuation = false;
if (ClosesWhitesmithsBlock && AdjustLevel == LineLevel::Remove)
--Line->Level;
diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h
index 3394bfab8b8e..5a6319f06041 100644
--- a/clang/lib/Format/UnwrappedLineParser.h
+++ b/clang/lib/Format/UnwrappedLineParser.h
@@ -49,6 +49,10 @@ struct UnwrappedLine {
bool MustBeDeclaration;
+ /// \c True if this line should be indented by ContinuationIndent in
+ /// addition to the normal indention level.
+ bool IsContinuation = false;
+
/// If this \c UnwrappedLine closes a block in a sequence of lines,
/// \c MatchingOpeningBlockLineIndex stores the index of the corresponding
/// opening line. Otherwise, \c MatchingOpeningBlockLineIndex must be
@@ -174,6 +178,11 @@ class UnwrappedLineParser {
bool tryToParsePropertyAccessor();
void tryToParseJSFunction();
bool tryToParseSimpleAttribute();
+ void parseVerilogHierarchyIdentifier();
+ void parseVerilogSensitivityList();
+ // Returns the number of levels of indentation in addition to the normal 1
+ // level for a block, used for indenting case labels.
+ unsigned parseVerilogHierarchyHeader();
// 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 30fe334a83f5..8b3b10c982ef 100644
--- a/clang/unittests/Format/FormatTestVerilog.cpp
+++ b/clang/unittests/Format/FormatTestVerilog.cpp
@@ -139,6 +139,75 @@ TEST_F(FormatTestVerilog, Delay) {
"x = x;"));
}
+TEST_F(FormatTestVerilog, Hierarchy) {
+ verifyFormat("module x;\n"
+ "endmodule");
+ // Test that the end label is on the same line as the end keyword.
+ verifyFormat("module x;\n"
+ "endmodule : x");
+ // Test that things inside are indented.
+ verifyFormat("module x;\n"
+ " generate\n"
+ " endgenerate\n"
+ "endmodule");
+ verifyFormat("program x;\n"
+ " generate\n"
+ " endgenerate\n"
+ "endprogram");
+ verifyFormat("interface x;\n"
+ " generate\n"
+ " endgenerate\n"
+ "endinterface");
+ verifyFormat("task x;\n"
+ " generate\n"
+ " endgenerate\n"
+ "endtask");
+ verifyFormat("function x;\n"
+ " generate\n"
+ " endgenerate\n"
+ "endfunction");
+ verifyFormat("class x;\n"
+ " generate\n"
+ " endgenerate\n"
+ "endclass");
+ // Test that they nest.
+ verifyFormat("module x;\n"
+ " program x;\n"
+ " program x;\n"
+ " endprogram\n"
+ " endprogram\n"
+ "endmodule");
+ // Test that an extern declaration doesn't change the indentation.
+ verifyFormat("extern module x;\n"
+ "x = x;");
+ // Test complex headers
+ verifyFormat("extern module x\n"
+ " import x.x::x::*;\n"
+ " import x;\n"
+ " #(parameter x)\n"
+ " (output x);");
+ verifyFormat("module x\n"
+ " import x.x::x::*;\n"
+ " import x;\n"
+ " #(parameter x)\n"
+ " (output x);\n"
+ " generate\n"
+ " endgenerate\n"
+ "endmodule : x");
+ verifyFormat("virtual class x\n"
+ " (x)\n"
+ " extends x(x)\n"
+ " implements x, x, x;\n"
+ " generate\n"
+ " endgenerate\n"
+ "endclass : x\n");
+ verifyFormat("function automatic logic [1 : 0] x\n"
+ " (input x);\n"
+ " generate\n"
+ " endgenerate\n"
+ "endfunction : x");
+}
+
TEST_F(FormatTestVerilog, If) {
verifyFormat("if (x)\n"
" x = x;");
diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp
index afc673961caf..9fe89eead213 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -831,6 +831,13 @@ TEST_F(TokenAnnotatorTest, UnderstandsVerilogOperators) {
ASSERT_EQ(Tokens.size(), 7u);
EXPECT_TOKEN(Tokens[1], tok::colon, TT_VerilogBlockLabelColon);
EXPECT_TOKEN(Tokens[4], tok::colon, TT_VerilogBlockLabelColon);
+ // Test that the dimension colon is annotated correctly.
+ Tokens = Annotate("var [1 : 0] x;");
+ ASSERT_EQ(Tokens.size(), 9u) << Tokens;
+ EXPECT_TOKEN(Tokens[3], tok::colon, TT_BitFieldColon);
+ Tokens = Annotate("extern function [1 : 0] x;");
+ ASSERT_EQ(Tokens.size(), 10u) << Tokens;
+ EXPECT_TOKEN(Tokens[4], tok::colon, TT_BitFieldColon);
}
} // namespace
More information about the cfe-commits
mailing list