diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -4181,6 +4181,16 @@ UT_Always }; + /// For Verilog, put each port on its own line in module instantiations. + /// \code + /// ffnand ff1(.q(), + /// .qbar(out1), + /// .clear(in1), + /// .preset(in2)); + /// \endcode + /// \version 17 + bool VerilogBreakBetweenInstancePorts; + /// The way to use tab characters in the resulting file. /// \version 3.7 UseTabStyle UseTab; @@ -4357,6 +4367,8 @@ StatementAttributeLikeMacros == R.StatementAttributeLikeMacros && StatementMacros == R.StatementMacros && TabWidth == R.TabWidth && TypenameMacros == R.TypenameMacros && UseTab == R.UseTab && + VerilogBreakBetweenInstancePorts == + R.VerilogBreakBetweenInstancePorts && WhitespaceSensitiveMacros == R.WhitespaceSensitiveMacros; } diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1035,6 +1035,8 @@ IO.mapOptional("TabWidth", Style.TabWidth); IO.mapOptional("TypenameMacros", Style.TypenameMacros); IO.mapOptional("UseTab", Style.UseTab); + IO.mapOptional("VerilogBreakBetweenInstancePorts", + Style.VerilogBreakBetweenInstancePorts); IO.mapOptional("WhitespaceSensitiveMacros", Style.WhitespaceSensitiveMacros); IO.mapOptional("Macros", Style.Macros); @@ -1456,6 +1458,7 @@ LLVMStyle.StatementMacros.push_back("QT_REQUIRE_VERSION"); LLVMStyle.TabWidth = 8; LLVMStyle.UseTab = FormatStyle::UT_Never; + LLVMStyle.VerilogBreakBetweenInstancePorts = true; LLVMStyle.WhitespaceSensitiveMacros.push_back("BOOST_PP_STRINGIZE"); LLVMStyle.WhitespaceSensitiveMacros.push_back("CF_SWIFT_NAME"); LLVMStyle.WhitespaceSensitiveMacros.push_back("NS_SWIFT_NAME"); 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,6 +152,9 @@ * 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) \ + /* list of port connections or parameters in a module instantiation */ \ + TYPE(VerilogInstancePortComma) \ + TYPE(VerilogInstancePortLParen) \ /* for the base in a number literal, not including the quote */ \ TYPE(VerilogNumberBase) \ /* like `(strong1, pull0)` */ \ 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 @@ -311,6 +311,9 @@ bool OperatorCalledAsMemberFunction = Prev->Previous && Prev->Previous->isOneOf(tok::period, tok::arrow); Contexts.back().IsExpression = OperatorCalledAsMemberFunction; + } else if (OpeningParen.is(TT_VerilogInstancePortLParen)) { + Contexts.back().IsExpression = true; + Contexts.back().ContextType = Context::VerilogInstancePortList; } else if (Style.isJavaScript() && (Line.startsWith(Keywords.kw_type, tok::identifier) || Line.startsWith(tok::kw_export, Keywords.kw_type, @@ -1143,6 +1146,25 @@ Tok->setType(TT_OverloadedOperatorLParen); } + if (Style.isVerilog()) { + const FormatToken *Prev = Tok->getPreviousNonComment(), *Prev2; + // Identify the parameter list and port list in a module instantiation. + // This is still needed when we already have + // UnwrappedLineParser::parseVerilogHierarchyHeader because that + // function is only responsible for the definition, not the + // instantiation. + if (Prev && (Prev2 = Prev->getPreviousNonComment()) && + ((Prev->is(tok::hash) && Keywords.isVerilogIdentifier(*Prev2)) || + (Keywords.isVerilogIdentifier(*Prev) && + (Prev2->is(tok::r_paren) || + Keywords.isVerilogIdentifier(*Prev2) || + (Prev2->endsSequence(tok::comma, tok::r_paren) && + (Prev2 = Prev2->getPreviousNonComment()->MatchingParen) && + Prev2->is(TT_VerilogInstancePortLParen)))))) { + Tok->setFinalizedType(TT_VerilogInstancePortLParen); + } + } + if (!parseParens()) return false; if (Line.MustBeDeclaration && Contexts.size() == 1 && @@ -1286,6 +1308,9 @@ case Context::InheritanceList: Tok->setType(TT_InheritanceComma); break; + case Context::VerilogInstancePortList: + Tok->setFinalizedType(TT_VerilogInstancePortComma); + break; default: if (Style.isVerilog() && Contexts.size() == 1 && Line.startsWith(Keywords.kw_assign)) { @@ -1673,6 +1698,8 @@ TemplateArgument, // C11 _Generic selection. C11GenericSelection, + // Like in the outer parentheses in `ffnand ff1(.q());`. + VerilogInstancePortList, } ContextType = Unknown; }; @@ -4738,6 +4765,15 @@ // Break between ports of different types. if (Left.is(TT_VerilogTypeComma)) return true; + // Break between ports in a module instantiation and after the parameter + // list. + if (Style.VerilogBreakBetweenInstancePorts && + (Left.is(TT_VerilogInstancePortComma) || + (Left.is(tok::r_paren) && Keywords.isVerilogIdentifier(Right) && + Left.MatchingParen && + Left.MatchingParen->is(TT_VerilogInstancePortLParen)))) { + 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/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -192,6 +192,7 @@ CHECK_PARSE_BOOL(SpaceBeforeJsonColon); CHECK_PARSE_BOOL(SpaceBeforeRangeBasedForLoopColon); CHECK_PARSE_BOOL(SpaceBeforeSquareBrackets); + CHECK_PARSE_BOOL(VerilogBreakBetweenInstancePorts); CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterCaseLabel); CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterClass); 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 @@ -659,6 +659,77 @@ " x = x;"); } +TEST_F(FormatTestVerilog, Instantiation) { + // Without ports. + verifyFormat("ffnand ff1;"); + // With named ports. + verifyFormat("ffnand ff1(.qbar(out1),\n" + " .clear(in1),\n" + " .preset(in2));"); + // With wildcard. + verifyFormat("ffnand ff1(.qbar(out1),\n" + " .clear(in1),\n" + " .preset(in2),\n" + " .*);"); + verifyFormat("ffnand ff1(.*,\n" + " .qbar(out1),\n" + " .clear(in1),\n" + " .preset(in2));"); + // With unconnected ports. + verifyFormat("ffnand ff1(.q(),\n" + " .qbar(out1),\n" + " .clear(in1),\n" + " .preset(in2));"); + verifyFormat("ffnand ff1(.q(),\n" + " .qbar(),\n" + " .clear(),\n" + " .preset());"); + verifyFormat("ffnand ff1(,\n" + " .qbar(out1),\n" + " .clear(in1),\n" + " .preset(in2));"); + // With positional ports. + verifyFormat("ffnand ff1(out1,\n" + " in1,\n" + " in2);"); + verifyFormat("ffnand ff1(,\n" + " out1,\n" + " in1,\n" + " in2);"); + // Multiple instantiations. + verifyFormat("ffnand ff1(.q(),\n" + " .qbar(out1),\n" + " .clear(in1),\n" + " .preset(in2)),\n" + " ff1(.q(),\n" + " .qbar(out1),\n" + " .clear(in1),\n" + " .preset(in2));"); + verifyFormat("ffnand //\n" + " ff1(.q(),\n" + " .qbar(out1),\n" + " .clear(in1),\n" + " .preset(in2)),\n" + " ff1(.q(),\n" + " .qbar(out1),\n" + " .clear(in1),\n" + " .preset(in2));"); + // With breaking between instance ports disabled. + auto Style = getDefaultStyle(); + Style.VerilogBreakBetweenInstancePorts = false; + verifyFormat("ffnand ff1;", Style); + verifyFormat("ffnand ff1(.qbar(out1), .clear(in1), .preset(in2), .*);", + Style); + verifyFormat("ffnand ff1(out1, in1, in2);", Style); + verifyFormat("ffnand ff1(.q(), .qbar(out1), .clear(in1), .preset(in2)),\n" + " ff1(.q(), .qbar(out1), .clear(in1), .preset(in2));", + Style); + verifyFormat("ffnand //\n" + " ff1(.q(), .qbar(out1), .clear(in1), .preset(in2)),\n" + " ff1(.q(), .qbar(out1), .clear(in1), .preset(in2));", + Style); +} + TEST_F(FormatTestVerilog, Operators) { // Test that unary operators are not followed by space. verifyFormat("x = +x;");