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 @@ -137,6 +137,10 @@ 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) @@ -1734,6 +1738,22 @@ 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.h b/clang/lib/Format/TokenAnnotator.h --- a/clang/lib/Format/TokenAnnotator.h +++ b/clang/lib/Format/TokenAnnotator.h @@ -43,6 +43,7 @@ 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 @@ /// \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/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -238,11 +238,13 @@ } 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 @@ // 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 @@ 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; } @@ -1229,6 +1234,8 @@ Tok->Next->isNot(tok::l_paren)) { Tok->setType(TT_CSharpGenericTypeConstraint); parseCSharpGenericTypeConstraint(); + if (Tok->getPreviousNonComment() == nullptr) + Line.IsContinuation = true; } break; case tok::arrow: @@ -1410,8 +1417,8 @@ 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(); @@ -3995,6 +4002,11 @@ (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) || @@ -4976,7 +4988,8 @@ } 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/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -69,7 +69,7 @@ } if (static_cast(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.h b/clang/lib/Format/UnwrappedLineParser.h --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -48,6 +48,10 @@ 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 @@ -173,6 +177,11 @@ 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/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -839,8 +839,13 @@ } }; + // 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 @@ // 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 @@ 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 @@ 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 @@ } if (Style.isVerilog()) { - if (Keywords.isVerilogBegin(*FormatTok)) { + if (Keywords.isVerilogBegin(*FormatTok) || + Keywords.isVerilogHierarchy(*FormatTok)) { parseBlock(); addUnwrappedLine(); return; @@ -3996,6 +4021,136 @@ 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() { + if (!FormatTok->is(tok::at)) + return; + 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(); + 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)) { + AddLevels += Style.IndentCaseLabels; + 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 @@ -4033,6 +4188,7 @@ Line->Tokens.clear(); Line->MatchingOpeningBlockLineIndex = UnwrappedLine::kInvalidIndex; Line->FirstStartColumn = 0; + Line->IsContinuation = false; if (ClosesWhitesmithsBlock && AdjustLevel == LineLevel::Remove) --Line->Level; 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 @@ -139,6 +139,75 @@ "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 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -894,6 +894,13 @@ 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