diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -1801,6 +1801,13 @@ kw_input, kw_output, kw_sequence))); } + /// Returns whether \p Tok is a Verilog keyword that starts a + /// structured procedure like 'always'. + bool isVerilogStructuredProcedure(const FormatToken &Tok) const { + return Tok.isOneOf(kw_always, kw_always_comb, kw_always_ff, kw_always_latch, + kw_final, kw_forever, kw_initial); + } + bool isVerilogQualifier(const FormatToken &Tok) const { switch (Tok.Tok.getKind()) { case tok::kw_extern: diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -4348,6 +4348,11 @@ (Left.is(TT_VerilogNumberBase) && Right.is(tok::numeric_constant))) { return false; } + // Don't add spaces between two at signs. Like in a coverage event. + // Don't add spaces between at and a sensitivity list like + // `@(posedge clk)`. + if (Left.is(tok::at) && Right.isOneOf(tok::l_paren, tok::star, tok::at)) + 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)) { diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -166,7 +166,7 @@ FormatToken *parseIfThenElse(IfStmtKind *IfKind, bool KeepBraces = false); void parseTryCatch(); void parseLoopBody(bool KeepBraces, bool WrapRightBrace); - void parseForOrWhileLoop(); + void parseForOrWhileLoop(bool HasParens = true); void parseDoWhile(); void parseLabel(bool LeftAlignLabel = false); void parseCaseLabel(); diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -1389,6 +1389,11 @@ } if (Style.isVerilog()) { + if (Keywords.isVerilogStructuredProcedure(*FormatTok)) { + parseForOrWhileLoop(/*HasParens=*/false); + return; + } + // Skip things that can exist before keywords like 'if' and 'case'. while (true) { if (FormatTok->isOneOf(Keywords.kw_priority, Keywords.kw_unique, @@ -2963,8 +2968,14 @@ NestedTooDeep.pop_back(); } -void UnwrappedLineParser::parseForOrWhileLoop() { - assert(FormatTok->isOneOf(tok::kw_for, tok::kw_while, TT_ForEachMacro) && +void UnwrappedLineParser::parseForOrWhileLoop(bool HasParens) { + assert((FormatTok->isOneOf(tok::kw_for, tok::kw_while, TT_ForEachMacro) || + (Style.isVerilog() && + FormatTok->isOneOf(Keywords.kw_always, Keywords.kw_always_comb, + Keywords.kw_always_ff, Keywords.kw_always_latch, + Keywords.kw_final, Keywords.kw_initial, + Keywords.kw_foreach, Keywords.kw_forever, + Keywords.kw_repeat))) && "'for', 'while' or foreach macro expected"); const bool KeepBraces = !Style.RemoveBracesLLVM || !FormatTok->isOneOf(tok::kw_for, tok::kw_while); @@ -2975,8 +2986,11 @@ nextToken(); if (Style.isCpp() && FormatTok->is(tok::kw_co_await)) nextToken(); - if (FormatTok->is(tok::l_paren)) + if (HasParens && FormatTok->is(tok::l_paren)) parseParens(); + // Event control. + if (Style.isVerilog()) + parseVerilogSensitivityList(); handleAttributes(); parseLoopBody(KeepBraces, /*WrapRightBrace=*/true); diff --git a/clang/unittests/Format/FormatTestVerilog.cpp b/clang/unittests/Format/FormatTestVerilog.cpp --- a/clang/unittests/Format/FormatTestVerilog.cpp +++ b/clang/unittests/Format/FormatTestVerilog.cpp @@ -252,6 +252,12 @@ Style); } +TEST_F(FormatTestVerilog, Coverage) { + verifyFormat("covergroup x\n" + " @@(begin x);\n" + "endgroup"); +} + TEST_F(FormatTestVerilog, Declaration) { verifyFormat("wire mynet;"); verifyFormat("wire mynet, mynet1;"); @@ -809,5 +815,50 @@ " endtable\n" "endprimitive"); } + +TEST_F(FormatTestVerilog, StructuredProcedure) { + // Blocks should be indented correctly. + verifyFormat("initial begin\n" + "end"); + verifyFormat("initial begin\n" + " x <= x;\n" + " x <= x;\n" + "end"); + verifyFormat("initial\n" + " x <= x;\n" + "x <= x;"); + verifyFormat("always @(x) begin\n" + "end"); + verifyFormat("always @(x) begin\n" + " x <= x;\n" + " x <= x;\n" + "end"); + verifyFormat("always @(x)\n" + " x <= x;\n" + "x <= x;"); + // Various keywords. + verifyFormat("always @(x)\n" + " x <= x;"); + verifyFormat("always @(posedge x)\n" + " x <= x;"); + verifyFormat("always\n" + " x <= x;"); + verifyFormat("always @*\n" + " x <= x;"); + verifyFormat("always @(*)\n" + " x <= x;"); + verifyFormat("always_comb\n" + " x <= x;"); + verifyFormat("always_latch @(x)\n" + " x <= x;"); + verifyFormat("always_ff @(posedge x)\n" + " x <= x;"); + verifyFormat("initial\n" + " x <= x;"); + verifyFormat("final\n" + " x <= x;"); + verifyFormat("forever\n" + " x <= x;"); +} } // namespace format } // end namespace clang