Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -935,6 +935,28 @@ /// For example: BOOST_FOREACH. std::vector ForEachMacros; + /// \brief A vector of macros that should be interpreted as complete + /// statements. + /// + /// Typical macros are expressions, and require a semi-colon to be + /// added; sometimes this is not the case, and this allows to make + /// clang-format aware of such cases. + /// + /// For example: Q_UNUSED + std::vector StatementMacros; + + /// \brief A vector of macros which are used to open namespace blocks. + /// + /// These are expected to be macros of the form: + /// \code + /// NAMESPACE(, ...) { + /// + /// } + /// \endcode + /// + /// For example: TESTSUITE + std::vector NamespaceMacros; + /// \brief See documentation of ``IncludeCategories``. struct IncludeCategory { /// \brief The regular expression that this category matches. @@ -1492,11 +1514,11 @@ MacroBlockEnd == R.MacroBlockEnd && MaxEmptyLinesToKeep == R.MaxEmptyLinesToKeep && NamespaceIndentation == R.NamespaceIndentation && + NamespaceMacros == R.NamespaceMacros && ObjCBlockIndentWidth == R.ObjCBlockIndentWidth && ObjCSpaceAfterProperty == R.ObjCSpaceAfterProperty && ObjCSpaceBeforeProtocolList == R.ObjCSpaceBeforeProtocolList && - PenaltyBreakAssignment == - R.PenaltyBreakAssignment && + PenaltyBreakAssignment == R.PenaltyBreakAssignment && PenaltyBreakBeforeFirstCallParameter == R.PenaltyBreakBeforeFirstCallParameter && PenaltyBreakComment == R.PenaltyBreakComment && @@ -1517,7 +1539,7 @@ SpacesInParentheses == R.SpacesInParentheses && SpacesInSquareBrackets == R.SpacesInSquareBrackets && Standard == R.Standard && TabWidth == R.TabWidth && - UseTab == R.UseTab; + StatementMacros == R.StatementMacros && UseTab == R.UseTab; } }; Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -361,6 +361,7 @@ IO.mapOptional("MacroBlockEnd", Style.MacroBlockEnd); IO.mapOptional("MaxEmptyLinesToKeep", Style.MaxEmptyLinesToKeep); IO.mapOptional("NamespaceIndentation", Style.NamespaceIndentation); + IO.mapOptional("NamespaceMacros", Style.NamespaceMacros); IO.mapOptional("ObjCBlockIndentWidth", Style.ObjCBlockIndentWidth); IO.mapOptional("ObjCSpaceAfterProperty", Style.ObjCSpaceAfterProperty); IO.mapOptional("ObjCSpaceBeforeProtocolList", @@ -395,6 +396,7 @@ IO.mapOptional("SpacesInParentheses", Style.SpacesInParentheses); IO.mapOptional("SpacesInSquareBrackets", Style.SpacesInSquareBrackets); IO.mapOptional("Standard", Style.Standard); + IO.mapOptional("StatementMacros", Style.StatementMacros); IO.mapOptional("TabWidth", Style.TabWidth); IO.mapOptional("UseTab", Style.UseTab); } @@ -619,6 +621,8 @@ LLVMStyle.DisableFormat = false; LLVMStyle.SortIncludes = true; + LLVMStyle.StatementMacros.push_back("Q_UNUSED"); + LLVMStyle.StatementMacros.push_back("QT_REQUIRE_VERSION"); return LLVMStyle; } Index: lib/Format/FormatToken.h =================================================================== --- lib/Format/FormatToken.h +++ lib/Format/FormatToken.h @@ -67,6 +67,7 @@ TYPE(LineComment) \ TYPE(MacroBlockBegin) \ TYPE(MacroBlockEnd) \ + TYPE(NamespaceMacro) \ TYPE(ObjCBlockLBrace) \ TYPE(ObjCBlockLParen) \ TYPE(ObjCDecl) \ @@ -83,6 +84,7 @@ TYPE(RegexLiteral) \ TYPE(SelectorName) \ TYPE(StartOfName) \ + TYPE(StatementMacro) \ TYPE(TemplateCloser) \ TYPE(TemplateOpener) \ TYPE(TemplateString) \ @@ -475,6 +477,21 @@ return MatchingParen && MatchingParen->opensBlockOrBlockTypeList(Style); } + /// \brief Return the actual namespace token, if this token starts a namespace + /// block. + const FormatToken *getNamespaceToken() const { + const FormatToken *NamespaceTok = this; + if (is(tok::comment)) + NamespaceTok = NamespaceTok->getNextNonComment(); + // Detect "(inline)? namespace" in the beginning of a line. + if (NamespaceTok && NamespaceTok->is(tok::kw_inline)) + NamespaceTok = NamespaceTok->getNextNonComment(); + return NamespaceTok && + NamespaceTok->isOneOf(tok::kw_namespace, TT_NamespaceMacro) + ? NamespaceTok + : nullptr; + } + private: // Disallow copying. FormatToken(const FormatToken &) = delete; Index: lib/Format/FormatTokenLexer.h =================================================================== --- lib/Format/FormatTokenLexer.h +++ lib/Format/FormatTokenLexer.h @@ -97,7 +97,29 @@ // Index (in 'Tokens') of the last token that starts a new line. unsigned FirstInLineIndex; SmallVector Tokens; - SmallVector ForEachMacros; + + struct MacroInfo { + MacroInfo() : Identifier(NULL), TokType(TT_Unknown) {} + MacroInfo(IdentifierInfo *Identifier, TokenType TokType) + : Identifier(Identifier), TokType(TokType) {} + + IdentifierInfo *Identifier; + TokenType TokType; + + bool operator==(const MacroInfo &Other) const { + return Identifier == Other.Identifier; + } + bool operator==(const IdentifierInfo *Other) const { + return Identifier == Other; + } + bool operator<(const MacroInfo &Other) const { + return Identifier < Other.Identifier; + } + bool operator<(const IdentifierInfo *Other) const { + return Identifier < Other; + } + }; + SmallVector Macros; bool FormattingDisabled; Index: lib/Format/FormatTokenLexer.cpp =================================================================== --- lib/Format/FormatTokenLexer.cpp +++ lib/Format/FormatTokenLexer.cpp @@ -37,8 +37,12 @@ Lex->SetKeepWhitespaceMode(true); for (const std::string &ForEachMacro : Style.ForEachMacros) - ForEachMacros.push_back(&IdentTable.get(ForEachMacro)); - std::sort(ForEachMacros.begin(), ForEachMacros.end()); + Macros.emplace_back(&IdentTable.get(ForEachMacro), TT_ForEachMacro); + for (const std::string &StatementMacro : Style.StatementMacros) + Macros.emplace_back(&IdentTable.get(StatementMacro), TT_StatementMacro); + for (const std::string &NamespaceMacro : Style.NamespaceMacros) + Macros.emplace_back(&IdentTable.get(NamespaceMacro), TT_NamespaceMacro); + std::sort(Macros.begin(), Macros.end()); } ArrayRef FormatTokenLexer::lex() { @@ -605,12 +609,13 @@ } if (Style.isCpp()) { + auto it = std::find(Macros.begin(), Macros.end(), + FormatTok->Tok.getIdentifierInfo()); if (!(Tokens.size() > 0 && Tokens.back()->Tok.getIdentifierInfo() && Tokens.back()->Tok.getIdentifierInfo()->getPPKeywordID() == tok::pp_define) && - std::find(ForEachMacros.begin(), ForEachMacros.end(), - FormatTok->Tok.getIdentifierInfo()) != ForEachMacros.end()) { - FormatTok->Type = TT_ForEachMacro; + it != Macros.end()) { + FormatTok->Type = it->TokType; } else if (FormatTok->is(tok::identifier)) { if (MacroBlockBeginRegex.match(Text)) { FormatTok->Type = TT_MacroBlockBegin; Index: lib/Format/NamespaceEndCommentsFixer.cpp =================================================================== --- lib/Format/NamespaceEndCommentsFixer.cpp +++ lib/Format/NamespaceEndCommentsFixer.cpp @@ -34,27 +34,52 @@ "namespace( +([a-zA-Z0-9:_]+))?\\.? *(\\*/)?$", llvm::Regex::IgnoreCase); +// Matches a valid namespace end comment. +// Valid namespace end comments don't need to be edited. +static llvm::Regex kNamespaceMacroCommentPattern = + llvm::Regex("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *" + "([a-zA-Z0-9_]+)\\(([a-zA-Z0-9:_]*)\\)\\.? *(\\*/)?$", + llvm::Regex::IgnoreCase); + // Computes the name of a namespace given the namespace token. // Returns "" for anonymous namespace. std::string computeName(const FormatToken *NamespaceTok) { - assert(NamespaceTok && NamespaceTok->is(tok::kw_namespace) && + assert(NamespaceTok && + NamespaceTok->isOneOf(tok::kw_namespace, TT_NamespaceMacro) && "expecting a namespace token"); std::string name = ""; - // Collects all the non-comment tokens between 'namespace' and '{'. const FormatToken *Tok = NamespaceTok->getNextNonComment(); - while (Tok && !Tok->is(tok::l_brace)) { - name += Tok->TokenText; - Tok = Tok->getNextNonComment(); + if (NamespaceTok->is(TT_NamespaceMacro)) { + // Collects all the non-comment tokens between opening parenthesis + // and closing parenthesis or comma + assert(Tok && Tok->is(tok::l_paren) && "expected an opening parenthesis"); + Tok = Tok ? Tok->getNextNonComment() : nullptr; + while (Tok && !Tok->isOneOf(tok::r_paren, tok::comma)) { + name += Tok->TokenText; + Tok = Tok->getNextNonComment(); + } + } else { + // Collects all the non-comment tokens between 'namespace' and '{'. + while (Tok && !Tok->is(tok::l_brace)) { + name += Tok->TokenText; + Tok = Tok->getNextNonComment(); + } } return name; } -std::string computeEndCommentText(StringRef NamespaceName, bool AddNewline) { - std::string text = "// namespace"; - if (!NamespaceName.empty()) { +std::string computeEndCommentText(StringRef NamespaceName, bool AddNewline, + const FormatToken *NamespaceTok) { + std::string text = "// "; + text += NamespaceTok->TokenText; + if (NamespaceTok->is(TT_NamespaceMacro)) + text += "("; + else if (!NamespaceName.empty()) text += ' '; - text += NamespaceName; - } + text += NamespaceName; + if (NamespaceTok->is(TT_NamespaceMacro)) + text += ")"; + // close brace if (AddNewline) text += '\n'; return text; @@ -64,22 +89,31 @@ return RBraceTok->Next && RBraceTok->Next->is(tok::comment); } -bool validEndComment(const FormatToken *RBraceTok, StringRef NamespaceName) { +bool validEndComment(const FormatToken *RBraceTok, StringRef NamespaceName, + const FormatToken *NamespaceTok) { assert(hasEndComment(RBraceTok)); const FormatToken *Comment = RBraceTok->Next; - SmallVector Groups; - if (kNamespaceCommentPattern.match(Comment->TokenText, &Groups)) { - StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] : ""; - // Anonymous namespace comments must not mention a namespace name. - if (NamespaceName.empty() && !NamespaceNameInComment.empty()) - return false; - StringRef AnonymousInComment = Groups.size() > 3 ? Groups[3] : ""; - // Named namespace comments must not mention anonymous namespace. - if (!NamespaceName.empty() && !AnonymousInComment.empty()) + SmallVector Groups; + if (NamespaceTok->is(TT_NamespaceMacro) && + kNamespaceMacroCommentPattern.match(Comment->TokenText, &Groups)) { + StringRef NamespaceTokenText = Groups.size() > 4 ? Groups[4] : ""; + // The name of the macro must be used + if (NamespaceTokenText != NamespaceTok->TokenText) return false; - return NamespaceNameInComment == NamespaceName; + } else if (NamespaceTok->isNot(tok::kw_namespace) || + !kNamespaceCommentPattern.match(Comment->TokenText, &Groups)) { + // Comment does not match regex + return false; } - return false; + StringRef NamespaceNameInComment = Groups.size() > 5 ? Groups[5] : ""; + // Anonymous namespace comments must not mention a namespace name. + if (NamespaceName.empty() && !NamespaceNameInComment.empty()) + return false; + StringRef AnonymousInComment = Groups.size() > 3 ? Groups[3] : ""; + // Named namespace comments must not mention anonymous namespace. + if (!NamespaceName.empty() && !AnonymousInComment.empty()) + return false; + return NamespaceNameInComment == NamespaceName; } void addEndComment(const FormatToken *RBraceTok, StringRef EndCommentText, @@ -109,21 +143,23 @@ } const FormatToken * -getNamespaceToken(const AnnotatedLine *line, +getNamespaceToken(const AnnotatedLine *Line, const SmallVectorImpl &AnnotatedLines) { - if (!line->Affected || line->InPPDirective || !line->startsWith(tok::r_brace)) + if (!Line->Affected || Line->InPPDirective || !Line->startsWith(tok::r_brace)) return nullptr; - size_t StartLineIndex = line->MatchingOpeningBlockLineIndex; + size_t StartLineIndex = Line->MatchingOpeningBlockLineIndex; if (StartLineIndex == UnwrappedLine::kInvalidIndex) return nullptr; assert(StartLineIndex < AnnotatedLines.size()); const FormatToken *NamespaceTok = AnnotatedLines[StartLineIndex]->First; - // Detect "(inline)? namespace" in the beginning of a line. - if (NamespaceTok->is(tok::kw_inline)) - NamespaceTok = NamespaceTok->getNextNonComment(); - if (!NamespaceTok || NamespaceTok->isNot(tok::kw_namespace)) - return nullptr; - return NamespaceTok; + return NamespaceTok->getNamespaceToken(); +} + +StringRef +getNamespaceTokenText(const AnnotatedLine *Line, + const SmallVectorImpl &AnnotatedLines) { + const FormatToken *NamespaceTok = getNamespaceToken(Line, AnnotatedLines); + return NamespaceTok ? NamespaceTok->TokenText : StringRef(); } } // namespace @@ -140,6 +176,7 @@ tooling::Replacements Fixes; std::string AllNamespaceNames = ""; size_t StartLineIndex = SIZE_MAX; + StringRef NamespaceTokenText; unsigned int CompactedNamespacesCount = 0; for (size_t I = 0, E = AnnotatedLines.size(); I != E; ++I) { const AnnotatedLine *EndLine = AnnotatedLines[I]; @@ -161,8 +198,11 @@ StartLineIndex = EndLine->MatchingOpeningBlockLineIndex; std::string NamespaceName = computeName(NamespaceTok); if (Style.CompactNamespaces) { + if (CompactedNamespacesCount == 0) + NamespaceTokenText = NamespaceTok->TokenText; if ((I + 1 < E) && - getNamespaceToken(AnnotatedLines[I + 1], AnnotatedLines) && + NamespaceTokenText == + getNamespaceTokenText(AnnotatedLines[I + 1], AnnotatedLines) && StartLineIndex - CompactedNamespacesCount - 1 == AnnotatedLines[I + 1]->MatchingOpeningBlockLineIndex && !AnnotatedLines[I + 1]->First->Finalized) { @@ -190,12 +230,13 @@ EndCommentNextTok->NewlinesBefore == 0 && EndCommentNextTok->isNot(tok::eof); const std::string EndCommentText = - computeEndCommentText(NamespaceName, AddNewline); + computeEndCommentText(NamespaceName, AddNewline, NamespaceTok); if (!hasEndComment(EndCommentPrevTok)) { bool isShort = I - StartLineIndex <= kShortNamespaceMaxLines + 1; if (!isShort) addEndComment(EndCommentPrevTok, EndCommentText, SourceMgr, &Fixes); - } else if (!validEndComment(EndCommentPrevTok, NamespaceName)) { + } else if (!validEndComment(EndCommentPrevTok, NamespaceName, + NamespaceTok)) { updateEndComment(EndCommentPrevTok, EndCommentText, SourceMgr, &Fixes); } StartLineIndex = SIZE_MAX; Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -925,7 +925,8 @@ TT_FunctionLBrace, TT_ImplicitStringLiteral, TT_InlineASMBrace, TT_JsFatArrow, TT_LambdaArrow, TT_OverloadedOperator, TT_RegexLiteral, - TT_TemplateString, TT_ObjCStringLiteral)) + TT_TemplateString, TT_ObjCStringLiteral, + TT_NamespaceMacro)) CurrentToken->Type = TT_Unknown; CurrentToken->Role.reset(); CurrentToken->MatchingParen = nullptr; Index: lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- lib/Format/UnwrappedLineFormatter.cpp +++ lib/Format/UnwrappedLineFormatter.cpp @@ -134,23 +134,29 @@ unsigned Indent = 0; }; -bool isNamespaceDeclaration(const AnnotatedLine *Line) { - const FormatToken *NamespaceTok = Line->First; - // Detect "(inline)? namespace" in the beginning of a line. - if (NamespaceTok->is(tok::kw_inline)) - NamespaceTok = NamespaceTok->getNextNonComment(); - return NamespaceTok && NamespaceTok->is(tok::kw_namespace); -} - -bool isEndOfNamespace(const AnnotatedLine *Line, - const SmallVectorImpl &AnnotatedLines) { +const FormatToken *getMatchingNamespaceToken( + const AnnotatedLine *Line, + const SmallVectorImpl &AnnotatedLines) { if (!Line->startsWith(tok::r_brace)) - return false; + return nullptr; size_t StartLineIndex = Line->MatchingOpeningBlockLineIndex; if (StartLineIndex == UnwrappedLine::kInvalidIndex) - return false; + return nullptr; assert(StartLineIndex < AnnotatedLines.size()); - return isNamespaceDeclaration(AnnotatedLines[StartLineIndex]); + return AnnotatedLines[StartLineIndex]->First->getNamespaceToken(); +} + +StringRef getNamespaceTokenText(const AnnotatedLine *Line) { + const FormatToken *NamespaceToken = Line->First->getNamespaceToken(); + return NamespaceToken ? NamespaceToken->TokenText : StringRef(); +} + +StringRef getMatchingNamespaceTokenText( + const AnnotatedLine *Line, + const SmallVectorImpl &AnnotatedLines) { + const FormatToken *NamespaceToken = + getMatchingNamespaceToken(Line, AnnotatedLines); + return NamespaceToken ? NamespaceToken->TokenText : StringRef(); } class LineJoiner { @@ -230,10 +236,11 @@ TheLine->Level != 0); if (Style.CompactNamespaces) { - if (isNamespaceDeclaration(TheLine)) { + if (auto nsToken = TheLine->First->getNamespaceToken()) { int i = 0; unsigned closingLine = TheLine->MatchingOpeningBlockLineIndex - 1; - for (; I + 1 + i != E && isNamespaceDeclaration(I[i + 1]) && + for (; I + 1 + i != E && + nsToken->TokenText == getNamespaceTokenText(I[i + 1]) && closingLine == I[i + 1]->MatchingOpeningBlockLineIndex && I[i + 1]->Last->TotalLength < Limit; i++, closingLine--) { @@ -245,10 +252,12 @@ return i; } - if (isEndOfNamespace(TheLine, AnnotatedLines)) { + if (auto nsToken = getMatchingNamespaceToken(TheLine, AnnotatedLines)) { int i = 0; unsigned openingLine = TheLine->MatchingOpeningBlockLineIndex - 1; - for (; I + 1 + i != E && isEndOfNamespace(I[i + 1], AnnotatedLines) && + for (; I + 1 + i != E && + nsToken->TokenText == + getMatchingNamespaceTokenText(I[i + 1], AnnotatedLines) && openingLine == I[i + 1]->MatchingOpeningBlockLineIndex; i++, openingLine--) { // No space between consecutive braces @@ -432,6 +441,7 @@ Tok->CanBreakBefore = true; return 1; } else if (Limit != 0 && !Line.startsWith(tok::kw_namespace) && + !Line.startsWith(TT_NamespaceMacro) && !startsExternCBlock(Line)) { // We don't merge short records. FormatToken *RecordTok = @@ -1004,6 +1014,7 @@ if (!Style.KeepEmptyLinesAtTheStartOfBlocks && PreviousLine && PreviousLine->Last->is(tok::l_brace) && PreviousLine->First->isNot(tok::kw_namespace) && + PreviousLine->First->isNot(TT_NamespaceMacro) && !startsExternCBlock(*PreviousLine)) Newlines = 1; Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -537,7 +537,7 @@ static bool ShouldBreakBeforeBrace(const FormatStyle &Style, const FormatToken &InitialToken) { - if (InitialToken.is(tok::kw_namespace)) + if (InitialToken.isOneOf(tok::kw_namespace, TT_NamespaceMacro)) return Style.BraceWrapping.AfterNamespace; if (InitialToken.is(tok::kw_class)) return Style.BraceWrapping.AfterClass; @@ -986,6 +986,19 @@ return; } } + if (Style.isCpp() && FormatTok->is(TT_StatementMacro)) { + nextToken(); + if (FormatTok->is(tok::l_paren)) + parseParens(); + if (FormatTok->is(tok::semi)) + nextToken(); + addUnwrappedLine(); + return; + } + if (Style.isCpp() && FormatTok->is(TT_NamespaceMacro)) { + parseNamespace(); + return; + } // In all other cases, parse the declaration. break; default: @@ -1625,12 +1638,17 @@ } void UnwrappedLineParser::parseNamespace() { - assert(FormatTok->Tok.is(tok::kw_namespace) && "'namespace' expected"); + assert(FormatTok->isOneOf(tok::kw_namespace, TT_NamespaceMacro) && + "'namespace' expected"); const FormatToken &InitialToken = *FormatTok; nextToken(); - while (FormatTok->isOneOf(tok::identifier, tok::coloncolon)) - nextToken(); + if (InitialToken.is(TT_NamespaceMacro)) { + parseParens(); + } else { + while (FormatTok->isOneOf(tok::identifier, tok::coloncolon)) + nextToken(); + } if (FormatTok->Tok.is(tok::l_brace)) { if (ShouldBreakBeforeBrace(Style, InitialToken)) addUnwrappedLine(); Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -1356,9 +1356,117 @@ Style)); } +TEST_F(FormatTest, NamespaceMacros) { + FormatStyle Style = getLLVMStyle(); + Style.NamespaceMacros.push_back("TESTSUITE"); + + verifyFormat("TESTSUITE(A) {\n" + "int foo();\n" + "} // TESTSUITE(A)", + Style); + + verifyFormat("TESTSUITE(A, B) {\n" + "int foo();\n" + "} // TESTSUITE(A)", + Style); + + // Properly indent according to NamespaceIndentation style + Style.NamespaceIndentation = FormatStyle::NI_All; + verifyFormat("TESTSUITE(A) {\n" + " int foo();\n" + "} // TESTSUITE(A)", + Style); + verifyFormat("TESTSUITE(A) {\n" + " namespace B {\n" + " int foo();\n" + " } // namespace B\n" + "} // TESTSUITE(A)", + Style); + verifyFormat("namespace A {\n" + " TESTSUITE(B) {\n" + " int foo();\n" + " } // TESTSUITE(B)\n" + "} // namespace A", + Style); + + Style.NamespaceIndentation = FormatStyle::NI_Inner; + verifyFormat("TESTSUITE(A) {\n" + "TESTSUITE(B) {\n" + " int foo();\n" + "} // TESTSUITE(B)\n" + "} // TESTSUITE(A)", + Style); + verifyFormat("TESTSUITE(A) {\n" + "namespace B {\n" + " int foo();\n" + "} // namespace B\n" + "} // TESTSUITE(A)", + Style); + verifyFormat("namespace A {\n" + "TESTSUITE(B) {\n" + " int foo();\n" + "} // TESTSUITE(B)\n" + "} // namespace A", + Style); + + // Properly merge namespace-macros blocks in CompactNamespaces mode + Style.NamespaceIndentation = FormatStyle::NI_None; + Style.CompactNamespaces = true; + verifyFormat("TESTSUITE(A) { TESTSUITE(B) {\n" + "}} // TESTSUITE(A::B)", + Style); + + EXPECT_EQ("TESTSUITE(out) { TESTSUITE(in) {\n" + "}} // TESTSUITE(out::in)", + format("TESTSUITE(out) {\n" + "TESTSUITE(in) {\n" + "} // TESTSUITE(in)\n" + "} // TESTSUITE(out)", + Style)); + + EXPECT_EQ("TESTSUITE(out) { TESTSUITE(in) {\n" + "}} // TESTSUITE(out::in)", + format("TESTSUITE(out) {\n" + "TESTSUITE(in) {\n" + "} // TESTSUITE(in)\n" + "} // TESTSUITE(out)", + Style)); + + // Do not merge different namespaces/macros + EXPECT_EQ("namespace out {\n" + "TESTSUITE(in) {\n" + "} // TESTSUITE(in)\n" + "} // namespace out", + format("namespace out {\n" + "TESTSUITE(in) {\n" + "} // TESTSUITE(in)\n" + "} // namespace out", + Style)); + EXPECT_EQ("TESTSUITE(out) {\n" + "namespace in {\n" + "} // namespace in\n" + "} // TESTSUITE(out)", + format("TESTSUITE(out) {\n" + "namespace in {\n" + "} // namespace in\n" + "} // TESTSUITE(out)", + Style)); + Style.NamespaceMacros.push_back("FOOBAR"); + EXPECT_EQ("TESTSUITE(out) {\n" + "FOOBAR(in) {\n" + "} // FOOBAR(in)\n" + "} // TESTSUITE(out)", + format("TESTSUITE(out) {\n" + "FOOBAR(in) {\n" + "} // FOOBAR(in)\n" + "} // TESTSUITE(out)", + Style)); +} + TEST_F(FormatTest, FormatsCompactNamespaces) { FormatStyle Style = getLLVMStyle(); Style.CompactNamespaces = true; + Style.NamespaceMacros.push_back("TESTSUITE"); verifyFormat("namespace A { namespace B {\n" "}} // namespace A::B", @@ -1372,6 +1480,14 @@ "} // namespace out", Style)); + EXPECT_EQ("namespace out { namespace in {\n" + "}} // namespace out::in", + format("namespace out {\n" + "namespace in {\n" + "} // namespace in\n" + "} // namespace out", + Style)); + // Only namespaces which have both consecutive opening and end get compacted EXPECT_EQ("namespace out {\n" "namespace in1 {\n" @@ -2172,6 +2288,26 @@ getLLVMStyleWithColumns(40))); verifyFormat("MACRO(>)"); + + // Some macros contain an implicit semicolon + FormatStyle Style = getLLVMStyle(); + Style.StatementMacros.push_back("FOO"); + verifyFormat("FOO(a) int b = 0;"); + verifyFormat("FOO(a)\n" + "int b = 0;", + Style); + verifyFormat("FOO(a);\n" + "int b = 0;", + Style); + verifyFormat("FOO(argc, argv, \"4.0.2\")\n" + "int b = 0;", + Style); + verifyFormat("FOO()\n" + "int b = 0;", + Style); + verifyFormat("FOO\n" + "int b = 0;", + Style); } TEST_F(FormatTest, LayoutMacroDefinitionsStatementsSpanningBlocks) { @@ -9555,6 +9691,18 @@ CHECK_PARSE("ForEachMacros: [BOOST_FOREACH, Q_FOREACH]", ForEachMacros, BoostAndQForeach); + Style.StatementMacros.clear(); + CHECK_PARSE("StatementMacros: [QUNUSED]", StatementMacros, + std::vector{"QUNUSED"}); + CHECK_PARSE("StatementMacros: [QUNUSED, QT_REQUIRE_VERSION]", StatementMacros, + std::vector({"QUNUSED", "QT_REQUIRE_VERSION"})); + + Style.NamespaceMacros.clear(); + CHECK_PARSE("NamespaceMacros: [TESTSUITE]", NamespaceMacros, + std::vector{"TESTSUITE"}); + CHECK_PARSE("NamespaceMacros: [TESTSUITE, SUITE]", NamespaceMacros, + std::vector({"TESTSUITE", "SUITE"})); + Style.IncludeCategories.clear(); std::vector ExpectedCategories = {{"abc/.*", 2}, {".*", 1}}; Index: unittests/Format/NamespaceEndCommentsFixerTest.cpp =================================================================== --- unittests/Format/NamespaceEndCommentsFixerTest.cpp +++ unittests/Format/NamespaceEndCommentsFixerTest.cpp @@ -53,6 +53,7 @@ " int i;\n" " int j;\n" "}")); + EXPECT_EQ("namespace {\n" " int i;\n" " int j;\n" @@ -249,6 +250,85 @@ "// unrelated")); } +TEST_F(NamespaceEndCommentsFixerTest, AddsMacroEndComment) { + FormatStyle Style = getLLVMStyle(); + Style.NamespaceMacros.push_back("TESTSUITE"); + + EXPECT_EQ("TESTSUITE() {\n" + " int i;\n" + " int j;\n" + "}// TESTSUITE()", + fixNamespaceEndComments("TESTSUITE() {\n" + " int i;\n" + " int j;\n" + "}", + Style)); + + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + " int j;\n" + "}// TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + " int j;\n" + "}", + Style)); + EXPECT_EQ("inline TESTSUITE(A) {\n" + " int i;\n" + " int j;\n" + "}// TESTSUITE(A)", + fixNamespaceEndComments("inline TESTSUITE(A) {\n" + " int i;\n" + " int j;\n" + "}", + Style)); + EXPECT_EQ("TESTSUITE(::A) {\n" + " int i;\n" + " int j;\n" + "}// TESTSUITE(::A)", + fixNamespaceEndComments("TESTSUITE(::A) {\n" + " int i;\n" + " int j;\n" + "}", + Style)); + EXPECT_EQ("TESTSUITE(::A::B) {\n" + " int i;\n" + " int j;\n" + "}// TESTSUITE(::A::B)", + fixNamespaceEndComments("TESTSUITE(::A::B) {\n" + " int i;\n" + " int j;\n" + "}", + Style)); + EXPECT_EQ("TESTSUITE(/**/::/**/A/**/::/**/B/**/) {\n" + " int i;\n" + " int j;\n" + "}// TESTSUITE(::A::B)", + fixNamespaceEndComments("TESTSUITE(/**/::/**/A/**/::/**/B/**/) {\n" + " int i;\n" + " int j;\n" + "}", + Style)); + EXPECT_EQ("TESTSUITE(A, B) {\n" + " int i;\n" + " int j;\n" + "}// TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A, B) {\n" + " int i;\n" + " int j;\n" + "}", + Style)); + EXPECT_EQ("TESTSUITE(\"Test1\") {\n" + " int i;\n" + " int j;\n" + "}// TESTSUITE(\"Test1\")", + fixNamespaceEndComments("TESTSUITE(\"Test1\") {\n" + " int i;\n" + " int j;\n" + "}", + Style)); +} + TEST_F(NamespaceEndCommentsFixerTest, AddsNewlineIfNeeded) { EXPECT_EQ("namespace A {\n" " int i;\n" @@ -381,6 +461,54 @@ "}; /* unnamed namespace */")); } +TEST_F(NamespaceEndCommentsFixerTest, KeepsValidMacroEndComment) { + FormatStyle Style = getLLVMStyle(); + Style.NamespaceMacros.push_back("TESTSUITE"); + + EXPECT_EQ("TESTSUITE() {\n" + " int i;\n" + "} // end anonymous TESTSUITE()", + fixNamespaceEndComments("TESTSUITE() {\n" + " int i;\n" + "} // end anonymous TESTSUITE()", + Style)); + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + "} /* end of TESTSUITE(A) */", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + "} /* end of TESTSUITE(A) */", + Style)); + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + "} // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + "} // TESTSUITE(A)", + Style)); + EXPECT_EQ("TESTSUITE(A::B) {\n" + " int i;\n" + "} // end TESTSUITE(A::B)", + fixNamespaceEndComments("TESTSUITE(A::B) {\n" + " int i;\n" + "} // end TESTSUITE(A::B)", + Style)); + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + "}; // end TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + "}; // end TESTSUITE(A)", + Style)); + EXPECT_EQ("TESTSUITE() {\n" + " int i;\n" + "}; /* unnamed TESTSUITE() */", + fixNamespaceEndComments("TESTSUITE() {\n" + " int i;\n" + "}; /* unnamed TESTSUITE() */", + Style)); +} + TEST_F(NamespaceEndCommentsFixerTest, UpdatesInvalidEndLineComment) { EXPECT_EQ("namespace {\n" " int i;\n" @@ -402,10 +530,10 @@ "} //")); EXPECT_EQ("namespace A {\n" " int i;\n" - "} // namespace A", + "}; // namespace A", fixNamespaceEndComments("namespace A {\n" " int i;\n" - "} //")); + "}; //")); EXPECT_EQ("namespace A {\n" " int i;\n" "} // namespace A", @@ -446,6 +574,96 @@ CompactNamespacesStyle)); } +TEST_F(NamespaceEndCommentsFixerTest, UpdatesInvalidMacroEndLineComment) { + FormatStyle Style = getLLVMStyle(); + Style.NamespaceMacros.push_back("TESTSUITE"); + + EXPECT_EQ("TESTSUITE() {\n" + " int i;\n" + "} // TESTSUITE()", + fixNamespaceEndComments("TESTSUITE() {\n" + " int i;\n" + "} // TESTSUITE(A)", + Style)); + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + "} // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + "} // TESTSUITE()", + Style)); + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + "} // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + "} //", + Style)); + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + "}; // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + "}; //", + Style)); + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + "} // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + "} // TESTSUITE A", + Style)); + EXPECT_EQ("TESTSUITE() {\n" + " int i;\n" + "} // TESTSUITE()", + fixNamespaceEndComments("TESTSUITE() {\n" + " int i;\n" + "} // TESTSUITE", + Style)); + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + "} // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + "} // TOASTSUITE(A)", + Style)); + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + "}; // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + "}; // TOASTSUITE(A)", + Style)); + // Updates invalid line comments even for short namespaces. + EXPECT_EQ("TESTSUITE(A) {} // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {} // TESTSUITE()", Style)); + EXPECT_EQ("TESTSUITE(A) {}; // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {}; // TESTSUITE()", Style)); + + // Update invalid comments for compacted namespaces. + FormatStyle CompactNamespacesStyle = getLLVMStyle(); + CompactNamespacesStyle.CompactNamespaces = true; + CompactNamespacesStyle.NamespaceMacros.push_back("TESTSUITE"); + + EXPECT_EQ("TESTSUITE(out) { TESTSUITE(in) {\n" + "}} // TESTSUITE(out::in)", + fixNamespaceEndComments("TESTSUITE(out) { TESTSUITE(in) {\n" + "}} // TESTSUITE(out)", + CompactNamespacesStyle)); + EXPECT_EQ("TESTSUITE(out) { TESTSUITE(in) {\n" + "}} // TESTSUITE(out::in)", + fixNamespaceEndComments("TESTSUITE(out) { TESTSUITE(in) {\n" + "}} // TESTSUITE(in)", + CompactNamespacesStyle)); + EXPECT_EQ("TESTSUITE(out) { TESTSUITE(in) {\n" + "}\n" + "} // TESTSUITE(out::in)", + fixNamespaceEndComments("TESTSUITE(out) { TESTSUITE(in) {\n" + "}// TAOSTSUITE(in)\n" + "} // TESTSUITE(out)", + CompactNamespacesStyle)); +} + TEST_F(NamespaceEndCommentsFixerTest, UpdatesInvalidEndBlockComment) { EXPECT_EQ("namespace {\n" " int i;\n" @@ -489,6 +707,58 @@ fixNamespaceEndComments("namespace A {}; /**/")); } +TEST_F(NamespaceEndCommentsFixerTest, UpdatesInvalidMacroEndBlockComment) { + FormatStyle Style = getLLVMStyle(); + Style.NamespaceMacros.push_back("TESTSUITE"); + + EXPECT_EQ("TESTSUITE() {\n" + " int i;\n" + "} // TESTSUITE()", + fixNamespaceEndComments("TESTSUITE() {\n" + " int i;\n" + "} /* TESTSUITE(A) */", + Style)); + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + "} // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + "} /* end TESTSUITE() */", + Style)); + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + "} // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + "} /**/", + Style)); + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + "} // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + "} /* end unnamed TESTSUITE() */", + Style)); + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + "} // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + "} /* TOASTSUITE(A) */", + Style)); + EXPECT_EQ("TESTSUITE(A) {\n" + " int i;\n" + "}; // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {\n" + " int i;\n" + "}; /* TAOSTSUITE(A) */", + Style)); + EXPECT_EQ("TESTSUITE(A) {} // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {} /**/", Style)); + EXPECT_EQ("TESTSUITE(A) {}; // TESTSUITE(A)", + fixNamespaceEndComments("TESTSUITE(A) {}; /**/", Style)); +} + TEST_F(NamespaceEndCommentsFixerTest, DoesNotAddEndCommentForNamespacesControlledByMacros) { EXPECT_EQ("#ifdef 1\n"