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 @@ -3707,9 +3707,11 @@ LeftParen = &Left; else if (Right.is(tok::r_paren) && Right.MatchingParen) LeftParen = Right.MatchingParen; - if (LeftParen && LeftParen->Previous && - isKeywordWithCondition(*LeftParen->Previous)) { - return true; + if (LeftParen) { + if (LeftParen->is(TT_ConditionLParen)) + return true; + if (LeftParen->Previous && isKeywordWithCondition(*LeftParen->Previous)) + return true; } } 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 @@ -163,7 +163,8 @@ void handleAttributes(); bool handleCppAttributes(); bool isBlockBegin(const FormatToken &Tok) const; - FormatToken *parseIfThenElse(IfStmtKind *IfKind, bool KeepBraces = false); + FormatToken *parseIfThenElse(IfStmtKind *IfKind, bool KeepBraces = false, + bool IsVerilogAssert = false); void parseTryCatch(); void parseLoopBody(bool KeepBraces, bool WrapRightBrace); void parseForOrWhileLoop(bool HasParens = true); 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 @@ -1393,6 +1393,15 @@ parseForOrWhileLoop(/*HasParens=*/false); return; } + if (FormatTok->isOneOf(Keywords.kw_foreach, Keywords.kw_repeat)) { + parseForOrWhileLoop(); + return; + } + if (FormatTok->isOneOf(tok::kw_restrict, Keywords.kw_assert, + Keywords.kw_assume, Keywords.kw_cover)) { + parseIfThenElse(IfKind, /*KeepBraces=*/false, /*IsVerilogAssert=*/true); + return; + } // Skip things that can exist before keywords like 'if' and 'case'. while (true) { @@ -2624,9 +2633,28 @@ } FormatToken *UnwrappedLineParser::parseIfThenElse(IfStmtKind *IfKind, - bool KeepBraces) { - assert(FormatTok->is(tok::kw_if) && "'if' expected"); + bool KeepBraces, + bool IsVerilogAssert) { + assert((FormatTok->is(tok::kw_if) || + (Style.isVerilog() && + FormatTok->isOneOf(tok::kw_restrict, Keywords.kw_assert, + Keywords.kw_assume, Keywords.kw_cover))) && + "'if' expected"); nextToken(); + + if (IsVerilogAssert) { + // Handle `assert #0` and `assert final`. + if (FormatTok->is(Keywords.kw_verilogHash)) { + nextToken(); + if (FormatTok->is(tok::numeric_constant)) + nextToken(); + } else if (FormatTok->isOneOf(Keywords.kw_final, Keywords.kw_property, + Keywords.kw_sequence)) { + nextToken(); + } + } + + // Handle `if !consteval`. if (FormatTok->is(tok::exclaim)) nextToken(); @@ -2637,10 +2665,18 @@ KeepIfBraces = !Style.RemoveBracesLLVM || KeepBraces; if (FormatTok->isOneOf(tok::kw_constexpr, tok::identifier)) nextToken(); - if (FormatTok->is(tok::l_paren)) + if (FormatTok->is(tok::l_paren)) { + FormatTok->setFinalizedType(TT_ConditionLParen); parseParens(); + } } handleAttributes(); + // The then action is optional in Verilog assert statements. + if (IsVerilogAssert && FormatTok->is(tok::semi)) { + nextToken(); + addUnwrappedLine(); + return nullptr; + } bool NeedsUnwrappedLine = false; keepAncestorBraces(); @@ -2658,6 +2694,8 @@ addUnwrappedLine(); else NeedsUnwrappedLine = true; + } else if (IsVerilogAssert && FormatTok->is(tok::kw_else)) { + addUnwrappedLine(); } else { parseUnbracedBody(); } @@ -2700,7 +2738,7 @@ markOptionalBraces(ElseLeftBrace); } addUnwrappedLine(); - } else if (FormatTok->is(tok::kw_if)) { + } else if (!IsVerilogAssert && FormatTok->is(tok::kw_if)) { const FormatToken *Previous = Tokens->getPreviousToken(); assert(Previous); const bool IsPrecededByComment = Previous->is(tok::comment); @@ -2993,8 +3031,14 @@ nextToken(); if (Style.isCpp() && FormatTok->is(tok::kw_co_await)) nextToken(); - if (HasParens && FormatTok->is(tok::l_paren)) + if (HasParens && FormatTok->is(tok::l_paren)) { + // The type is only set for Verilog basically because we were afraid to + // change the existing behavior for loops. See the discussion on D121756 for + // details. + if (Style.isVerilog()) + FormatTok->setFinalizedType(TT_ConditionLParen); parseParens(); + } // Event control. if (Style.isVerilog()) parseVerilogSensitivityList(); 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 @@ -671,6 +671,107 @@ " x = x;"); verifyFormat("(* x, x = \"x\" *) if (x)\n" " x = x;"); + + // Assert are treated similar to if. But the else parts should not be + // chained. + verifyFormat("assert (x);"); + verifyFormat("assert (x)\n" + " $info();"); + verifyFormat("assert (x)\n" + " $info();\n" + "else\n" + " $error();"); + verifyFormat("assert (x)\n" + "else\n" + " $error();"); + verifyFormat("assert (x)\n" + "else begin\n" + "end"); + verifyFormat("assert (x)\n" + "else\n" + " if (x)\n" + " $error();"); + verifyFormat("assert (x)\n" + " $info();\n" + "else\n" + " if (x)\n" + " $error();"); + verifyFormat("assert (x)\n" + " $info();\n" + "else\n" + " if (x)\n" + " $error();\n" + " else\n" + " $error();"); + verifyFormat("assert (x)\n" + " $info();\n" + "else\n" + " if (x)\n" + " $error();\n" + " else if (x)\n" + " $error();\n" + " else\n" + " $error();"); + // The body is optional for asserts. The next line should not be indented if + // the statement already ended with a semicolon. + verifyFormat("assert (x);\n" + "x = x;"); + verifyFormat("if (x)\n" + " assert (x);\n" + "else if (x) begin\n" + "end else begin\n" + "end"); + verifyFormat("if (x)\n" + " assert (x);\n" + "else begin\n" + "end"); + verifyFormat("if (x)\n" + " assert (x)\n" + " else begin\n" + " end"); + // Other keywords. + verifyFormat("assume (x)\n" + " $info();"); + verifyFormat("cover (x)\n" + " $info();"); + verifyFormat("restrict (x)\n" + " $info();"); + verifyFormat("assert #0 (x)\n" + " $info();"); + verifyFormat("assert final (x)\n" + " $info();"); + verifyFormat("cover #0 (x)\n" + " $info();"); + verifyFormat("cover final (x)\n" + " $info();"); + + // The space around parentheses options should work. + auto Style = getDefaultStyle(); + verifyFormat("if (x)\n" + " x = x;\n" + "else if (x)\n" + " x = x;", + Style); + verifyFormat("assert (x);", Style); + verifyFormat("assert #0 (x);", Style); + verifyFormat("assert (x)\n" + "else\n" + " if (x)\n" + " x = x;", + Style); + Style.SpacesInConditionalStatement = true; + verifyFormat("if ( x )\n" + " x = x;\n" + "else if ( x )\n" + " x = x;", + Style); + verifyFormat("assert ( x );", Style); + verifyFormat("assert #0 ( x );", Style); + verifyFormat("assert ( x )\n" + "else\n" + " if ( x )\n" + " x = x;", + Style); } TEST_F(FormatTestVerilog, Instantiation) { @@ -744,6 +845,25 @@ Style); } +TEST_F(FormatTestVerilog, Loop) { + verifyFormat("foreach (x[x])\n" + " x = x;"); + verifyFormat("repeat (x)\n" + " x = x;"); + verifyFormat("foreach (x[x]) begin\n" + "end"); + verifyFormat("repeat (x) begin\n" + "end"); + auto Style = getDefaultStyle(); + Style.SpacesInConditionalStatement = true; + verifyFormat("foreach ( x[x] )\n" + " x = x;", + Style); + verifyFormat("repeat ( x )\n" + " x = x;", + Style); +} + TEST_F(FormatTestVerilog, Operators) { // Test that unary operators are not followed by space. verifyFormat("x = +x;"); diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -841,7 +841,7 @@ " [[nodiscard]] constexpr operator T() const { " "return number_zero_v; }\n" "};"); - ASSERT_EQ(Tokens.size(), 44u); + ASSERT_EQ(Tokens.size(), 44u) << Tokens; EXPECT_TOKEN(Tokens[13], tok::kw_requires, TT_RequiresClause); EXPECT_TOKEN(Tokens[14], tok::kw_requires, TT_RequiresExpression); EXPECT_TOKEN(Tokens[15], tok::l_brace, TT_RequiresExpressionLBrace); @@ -1605,7 +1605,7 @@ // Test for block label colons. Tokens = Annotate("begin : x\n" "end : x"); - ASSERT_EQ(Tokens.size(), 7u); + ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::colon, TT_VerilogBlockLabelColon); EXPECT_TOKEN(Tokens[4], tok::colon, TT_VerilogBlockLabelColon); // Test that the dimension colon is annotated correctly. @@ -1632,15 +1632,15 @@ EXPECT_TOKEN(Tokens[9], tok::colon, TT_GotoLabelColon); // Non-blocking assignments. Tokens = Annotate("a <= b;"); - ASSERT_EQ(Tokens.size(), 5u); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::lessequal, TT_BinaryOperator); EXPECT_TOKEN_PRECEDENCE(Tokens[1], prec::Assignment); Tokens = Annotate("if (a <= b) break;"); - ASSERT_EQ(Tokens.size(), 9u); + ASSERT_EQ(Tokens.size(), 9u) << Tokens; EXPECT_TOKEN(Tokens[3], tok::lessequal, TT_BinaryOperator); EXPECT_TOKEN_PRECEDENCE(Tokens[3], prec::Relational); Tokens = Annotate("a <= b <= a;"); - ASSERT_EQ(Tokens.size(), 7u); + ASSERT_EQ(Tokens.size(), 7u) << Tokens; EXPECT_TOKEN(Tokens[1], tok::lessequal, TT_BinaryOperator); EXPECT_TOKEN_PRECEDENCE(Tokens[1], prec::Assignment); EXPECT_TOKEN(Tokens[3], tok::lessequal, TT_BinaryOperator); @@ -1648,15 +1648,35 @@ // Port lists in module instantiation. Tokens = Annotate("module_x instance_1(port_1), instance_2(port_2);"); - ASSERT_EQ(Tokens.size(), 12u); + ASSERT_EQ(Tokens.size(), 12u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::l_paren, TT_VerilogInstancePortLParen); EXPECT_TOKEN(Tokens[7], tok::l_paren, TT_VerilogInstancePortLParen); Tokens = Annotate("module_x #(parameter) instance_1(port_1), " "instance_2(port_2);"); - ASSERT_EQ(Tokens.size(), 16u); + ASSERT_EQ(Tokens.size(), 16u) << Tokens; EXPECT_TOKEN(Tokens[2], tok::l_paren, TT_VerilogInstancePortLParen); EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_VerilogInstancePortLParen); EXPECT_TOKEN(Tokens[11], tok::l_paren, TT_VerilogInstancePortLParen); + + // Condition parentheses. + Tokens = Annotate("assert (x);"); + ASSERT_EQ(Tokens.size(), 6u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::l_paren, TT_ConditionLParen); + Tokens = Annotate("assert #0 (x);"); + ASSERT_EQ(Tokens.size(), 8u) << Tokens; + EXPECT_TOKEN(Tokens[3], tok::l_paren, TT_ConditionLParen); + Tokens = Annotate("assert final (x);"); + ASSERT_EQ(Tokens.size(), 7u) << Tokens; + EXPECT_TOKEN(Tokens[2], tok::l_paren, TT_ConditionLParen); + Tokens = Annotate("foreach (x[x]) continue;"); + ASSERT_EQ(Tokens.size(), 10u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::l_paren, TT_ConditionLParen); + Tokens = Annotate("repeat (x[x]) continue;"); + ASSERT_EQ(Tokens.size(), 10u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::l_paren, TT_ConditionLParen); + Tokens = Annotate("case (x) endcase;"); + ASSERT_EQ(Tokens.size(), 7u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::l_paren, TT_ConditionLParen); } TEST_F(TokenAnnotatorTest, UnderstandConstructors) { @@ -1688,6 +1708,22 @@ EXPECT_TOKEN(Tokens[13], tok::l_brace, TT_FunctionLBrace); } +TEST_F(TokenAnnotatorTest, UnderstandsConditionParens) { + auto Tokens = annotate("if (x) {}"); + ASSERT_EQ(Tokens.size(), 7u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::l_paren, TT_ConditionLParen); + Tokens = annotate("if constexpr (x) {}"); + ASSERT_EQ(Tokens.size(), 8u) << Tokens; + EXPECT_TOKEN(Tokens[2], tok::l_paren, TT_ConditionLParen); + Tokens = annotate("if CONSTEXPR (x) {}"); + ASSERT_EQ(Tokens.size(), 8u) << Tokens; + EXPECT_TOKEN(Tokens[2], tok::l_paren, TT_ConditionLParen); + Tokens = annotate("if (x) {} else if (x) {}"); + ASSERT_EQ(Tokens.size(), 14u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::l_paren, TT_ConditionLParen); + EXPECT_TOKEN(Tokens[8], tok::l_paren, TT_ConditionLParen); +} + } // namespace } // namespace format } // namespace clang