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 @@ -152,8 +152,12 @@ TYPE(VerilogDimensionedTypeName) \ /* for the base in a number literal, not including the quote */ \ TYPE(VerilogNumberBase) \ + /* like `(strong1, pull0)` */ \ + TYPE(VerilogStrength) \ /* Things inside the table in user-defined primitives. */ \ TYPE(VerilogTableItem) \ + /* those that separate ports of different types */ \ + TYPE(VerilogTypeComma) \ TYPE(Unknown) /// Determines the semantic type of a syntactic token, e.g. whether "<" is a @@ -743,8 +747,8 @@ } /// Returns the next token ignoring comments. - [[nodiscard]] const FormatToken *getNextNonComment() const { - const FormatToken *Tok = Next; + [[nodiscard]] FormatToken *getNextNonComment() const { + FormatToken *Tok = Next; while (Tok && Tok->is(tok::comment)) Tok = Tok->Next; return Tok; @@ -1792,6 +1796,27 @@ kw_input, kw_output, kw_sequence))); } + bool isVerilogQualifier(const FormatToken &Tok) const { + switch (Tok.Tok.getKind()) { + case tok::kw_extern: + case tok::kw_signed: + case tok::kw_static: + case tok::kw_unsigned: + case tok::kw_virtual: + return true; + case tok::identifier: + return Tok.isOneOf( + kw_let, kw_var, kw_ref, kw_automatic, kw_bins, kw_coverpoint, + kw_ignore_bins, kw_illegal_bins, kw_inout, kw_input, kw_interconnect, + kw_local, kw_localparam, kw_output, kw_parameter, kw_pure, kw_rand, + kw_randc, kw_scalared, kw_specparam, kw_tri, kw_tri0, kw_tri1, + kw_triand, kw_trior, kw_trireg, kw_uwire, kw_vectored, kw_wand, + kw_wildcard, kw_wire, kw_wor); + default: + return false; + } + } + private: /// The JavaScript keywords beyond the C++ keyword set. std::unordered_set JsExtraKeywords; 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 @@ -2590,8 +2590,21 @@ FormatToken *Start = Current; FormatToken *LatestOperator = nullptr; unsigned OperatorIndex = 0; + // The first name of the current type in a port list. + FormatToken *VerilogFirstOfType = nullptr; while (Current) { + // In Verilog ports in a module header that don't have a type take the + // type of the previous one. For example, + // module a(output b, + // c, + // output d); + // In this case there need to be fake parentheses around b and c. + if (Style.isVerilog() && Precedence == prec::Comma) { + VerilogFirstOfType = + verilogGroupDecl(VerilogFirstOfType, LatestOperator); + } + // Consume operators with higher precedence. parse(Precedence + 1); @@ -2604,7 +2617,7 @@ Start = Current; } - // At the end of the line or when an operator with higher precedence is + // At the end of the line or when an operator with lower precedence is // found, insert fake parenthesis and return. if (!Current || (Current->closesScope() && @@ -2641,6 +2654,12 @@ } } + // Group variables of the same type. + if (Style.isVerilog() && Precedence == prec::Comma && + VerilogFirstOfType != nullptr) { + addFakeParenthesis(VerilogFirstOfType, prec::Comma); + } + if (LatestOperator && (Current || Precedence > 0)) { // The requires clauses do not neccessarily end in a semicolon or a brace, // but just go over to struct/class or a function declaration, we need to @@ -2776,6 +2795,144 @@ } } + // Add fake parenthesis around declarations of the same type for example in a + // module prototype. Return the first port / variable of the current type. + FormatToken *verilogGroupDecl(FormatToken *FirstOfType, + FormatToken *PreviousComma) { + if (Current == nullptr) + return nullptr; + + FormatToken *Start = Current; + + // Skip attributes. + while (Start->startsSequence(tok::l_paren, tok::star)) { + if ((Start = Start->MatchingParen) == nullptr || + (Start = Start->getNextNonComment()) == nullptr) { + return nullptr; + } + } + + FormatToken *Tok = Start; + + if (Tok->is(Keywords.kw_assign)) + Tok = Tok->getNextNonComment(); + + // Skip any type qualifiers to find the first identifier. It may be either a + // new type name or a variable name. There can be several type qualifiers + // preceding a variable name, and we can not tell them apart by looking at + // the word alone since a macro can be defined as either a type qualifier or + // a variable name. Thus we use the last word before the dimensions instead + // of the first word as the candidate for the variable or type name. + FormatToken *First = nullptr; + while (Tok != nullptr) { + FormatToken *Next = Tok->getNextNonComment(); + + if (Tok->is(tok::hash)) { + // Start of a macro expansion. + First = Tok; + Tok = Next; + if (Tok != nullptr) + Tok = Tok->getNextNonComment(); + } else if (Tok->is(tok::hashhash)) { + // Concatenation. Skip. + Tok = Next; + if (Tok != nullptr) + Tok = Tok->getNextNonComment(); + } else if ((Keywords.isVerilogQualifier(*Tok) || + Keywords.isVerilogIdentifier(*Tok))) { + First = Tok; + Tok = Next; + // The name may have dots like `interface_foo.modport_foo`. + while (Tok != nullptr && Tok->isOneOf(tok::period, tok::coloncolon) && + (Tok = Tok->getNextNonComment())) { + if (Keywords.isVerilogIdentifier(*Tok)) + Tok = Tok->getNextNonComment(); + } + } else if (Next == nullptr) { + Tok = nullptr; + } else if (Tok->is(tok::l_paren)) { + // Make sure the parenthesized list is a drive strength. Otherwise the + // statement may be a module instantiation in which case we have already + // found the instance name. + if (Next->isOneOf( + Keywords.kw_highz0, Keywords.kw_highz1, Keywords.kw_large, + Keywords.kw_medium, Keywords.kw_pull0, Keywords.kw_pull1, + Keywords.kw_small, Keywords.kw_strong0, Keywords.kw_strong1, + Keywords.kw_supply0, Keywords.kw_supply1, Keywords.kw_weak0, + Keywords.kw_weak1)) { + Tok->setType(TT_VerilogStrength); + Tok = Tok->MatchingParen; + if (Tok != nullptr) { + Tok->setType(TT_VerilogStrength); + Tok = Tok->getNextNonComment(); + } + } else { + break; + } + } else if (Tok->is(tok::hash)) { + if (Next->is(tok::l_paren)) + Next = Next->MatchingParen; + if (Next != nullptr) + Tok = Next->getNextNonComment(); + } else { + break; + } + } + + // Find the second identifier. If it exists it will be the name. + FormatToken *Second = nullptr; + // Dimensions. + while (Tok != nullptr && Tok->is(tok::l_square) && + (Tok = Tok->MatchingParen) != nullptr) { + Tok = Tok->getNextNonComment(); + } + if (Tok != nullptr && + (Tok->is(tok::hash) || Keywords.isVerilogIdentifier(*Tok))) { + Second = Tok; + } + + // If the second identifier doesn't exist and there are qualifiers, the type + // is implied. + FormatToken *TypedName = nullptr; + if (Second != nullptr) { + TypedName = Second; + if (First != nullptr && First->is(TT_Unknown)) + First->setType(TT_VerilogDimensionedTypeName); + } else if (First != Start) { + // If 'First' is null, then this isn't a declaration, 'TypedName' gets set + // to null as intended. + TypedName = First; + } + + if (TypedName != nullptr) { + // This is a declaration with a new type. + if (TypedName->is(TT_Unknown)) + TypedName->setType(TT_StartOfName); + // Group variables of the previous type. + if (FirstOfType != nullptr && PreviousComma != nullptr) { + PreviousComma->setType(TT_VerilogTypeComma); + addFakeParenthesis(FirstOfType, prec::Comma, PreviousComma->Previous); + } + + FirstOfType = TypedName; + + // Don't let higher precedence handle the qualifiers. For example if we + // have: + // parameter x = 0 + // We skip `parameter` here. This way the fake parentheses for the + // assignment will be around `x = 0`. + while (Current != nullptr && Current != FirstOfType) { + if (Current->opensScope()) { + next(); + parse(); + } + next(); + } + } + + return FirstOfType; + } + const FormatStyle &Style; const AdditionalKeywords &Keywords; const AnnotatedLine &Line; @@ -4203,6 +4360,9 @@ // Add space in attribute like `(* ASYNC_REG = "TRUE" *)`. if (Left.endsSequence(tok::star, tok::l_paren) && Right.is(tok::identifier)) return true; + // Add space before drive strength like in `wire (strong1, pull0)`. + if (Right.is(tok::l_paren) && Right.is(TT_VerilogStrength)) + return true; } if (Left.is(TT_ImplicitStringLiteral)) return Right.hasWhitespaceBefore(); @@ -4514,6 +4674,9 @@ return true; } } else if (Style.isVerilog()) { + // Break between ports of different types. + if (Left.is(TT_VerilogTypeComma)) + return true; // 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)) 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 @@ -275,6 +275,76 @@ "x = x;")); } +TEST_F(FormatTestVerilog, Headers) { + // Test headers with multiple ports. + verifyFormat("module mh1\n" + " (input var int in1,\n" + " input var shortreal in2,\n" + " output tagged_st out);\n" + "endmodule"); + // Ports should be grouped by types. + verifyFormat("module test\n" + " (input [7 : 0] a,\n" + " input signed [7 : 0] b, c, d);\n" + "endmodule"); + verifyFormat("module test\n" + " (input [7 : 0] a,\n" + " (* x = x *) input signed [7 : 0] b, c, d);\n" + "endmodule"); + verifyFormat("module test\n" + " (input [7 : 0] a = 0,\n" + " input signed [7 : 0] b = 0, c = 0, d = 0);\n" + "endmodule"); + verifyFormat("module test\n" + " #(parameter x)\n" + " (input [7 : 0] a,\n" + " input signed [7 : 0] b, c, d);\n" + "endmodule"); + // When a line needs to be broken, ports of the same type should be aligned to + // the same column. + verifyFormat("module test\n" + " (input signed [7 : 0] b, c, //\n" + " d);\n" + "endmodule"); + verifyFormat("module test\n" + " ((* x = x *) input signed [7 : 0] b, c, //\n" + " d);\n" + "endmodule"); + verifyFormat("module test\n" + " (input signed [7 : 0] b = 0, c, //\n" + " d);\n" + "endmodule"); + verifyFormat("module test\n" + " (input signed [7 : 0] b, c = 0, //\n" + " d);\n" + "endmodule"); + verifyFormat("module test\n" + " (input signed [7 : 0] b, c, //\n" + " d = 0);\n" + "endmodule"); + verifyFormat("module test\n" + " (input wire logic signed [7 : 0][0 : 1] b, c, //\n" + " d);\n" + "endmodule"); + verifyFormat("module test\n" + " (input signed [7 : 0] b, //\n" + " c, //\n" + " d);\n" + "endmodule"); + verifyFormat("module test\n" + " (input [7 : 0] a,\n" + " input signed [7 : 0] b, //\n" + " c, //\n" + " d);\n" + "endmodule"); + verifyFormat("module test\n" + " (input signed [7 : 0] b, //\n" + " c, //\n" + " d,\n" + " output signed [7 : 0] h);\n" + "endmodule"); +} + TEST_F(FormatTestVerilog, Hierarchy) { verifyFormat("module x;\n" "endmodule");