clang-format: better handle namespace macros
authorFrancois Ferrand <thetypz@gmail.com>
Thu, 6 Jun 2019 20:06:23 +0000 (20:06 +0000)
committerFrancois Ferrand <thetypz@gmail.com>
Thu, 6 Jun 2019 20:06:23 +0000 (20:06 +0000)
Summary:
Other macros are used to declare namespaces, and should thus be handled
similarly. This is the case for crpcut's TESTSUITE macro, or for
unittest-cpp's SUITE macro:

      TESTSUITE(Foo) {
      TEST(MyFirstTest) {
        assert(0);
      }
      } // TESTSUITE(Foo)

This patch deals with this cases by introducing a new option to specify
lists of namespace macros. Internally, it re-uses the system already in
place for foreach and statement macros, to ensure there is no impact on
performance.

Reviewers: krasimir, djasper, klimek

Reviewed By: klimek

Subscribers: acoomans, cfe-commits, klimek

Tags: #clang

Differential Revision: https://reviews.llvm.org/D37813

llvm-svn: 362740

12 files changed:
clang/docs/ClangFormatStyleOptions.rst
clang/include/clang/Format/Format.h
clang/lib/Format/Format.cpp
clang/lib/Format/FormatToken.h
clang/lib/Format/FormatTokenLexer.cpp
clang/lib/Format/NamespaceEndCommentsFixer.cpp
clang/lib/Format/TokenAnnotator.cpp
clang/lib/Format/TokenAnnotator.h
clang/lib/Format/UnwrappedLineFormatter.cpp
clang/lib/Format/UnwrappedLineParser.cpp
clang/unittests/Format/FormatTest.cpp
clang/unittests/Format/NamespaceEndCommentsFixerTest.cpp

index 8bd0e9c..1276a41 100644 (file)
@@ -1782,6 +1782,19 @@ the configuration (without a prefix: ``Auto``).
 
 
 
+**NamespaceMacros** (``std::vector<std::string>``)
+  A vector of macros which are used to open namespace blocks.
+
+  These are expected to be macros of the form:
+
+  .. code-block:: c++
+
+    NAMESPACE(<namespace-name>, ...) {
+      <namespace-content>
+    }
+
+  For example: TESTSUITE
+
 **ObjCBinPackProtocolList** (``BinPackStyle``)
   Controls bin-packing Objective-C protocol conformance list
   items into as few lines as possible when they go over ``ColumnLimit``.
index 0aae752..c5034db 100644 (file)
@@ -1195,6 +1195,18 @@ struct FormatStyle {
   /// For example: Q_UNUSED
   std::vector<std::string> StatementMacros;
 
+  /// A vector of macros which are used to open namespace blocks.
+  ///
+  /// These are expected to be macros of the form:
+  /// \code
+  ///   NAMESPACE(<namespace-name>, ...) {
+  ///     <namespace-content>
+  ///   }
+  /// \endcode
+  ///
+  /// For example: TESTSUITE
+  std::vector<std::string> NamespaceMacros;
+
   tooling::IncludeStyle IncludeStyle;
 
   /// Indent case labels one level from the switch statement.
@@ -1942,6 +1954,7 @@ struct FormatStyle {
            MacroBlockEnd == R.MacroBlockEnd &&
            MaxEmptyLinesToKeep == R.MaxEmptyLinesToKeep &&
            NamespaceIndentation == R.NamespaceIndentation &&
+           NamespaceMacros == R.NamespaceMacros &&
            ObjCBinPackProtocolList == R.ObjCBinPackProtocolList &&
            ObjCBlockIndentWidth == R.ObjCBlockIndentWidth &&
            ObjCSpaceAfterProperty == R.ObjCSpaceAfterProperty &&
index d775ca1..31442b5 100644 (file)
@@ -455,6 +455,7 @@ template <> struct MappingTraits<FormatStyle> {
     IO.mapOptional("MacroBlockEnd", Style.MacroBlockEnd);
     IO.mapOptional("MaxEmptyLinesToKeep", Style.MaxEmptyLinesToKeep);
     IO.mapOptional("NamespaceIndentation", Style.NamespaceIndentation);
+    IO.mapOptional("NamespaceMacros", Style.NamespaceMacros);
     IO.mapOptional("ObjCBinPackProtocolList", Style.ObjCBinPackProtocolList);
     IO.mapOptional("ObjCBlockIndentWidth", Style.ObjCBlockIndentWidth);
     IO.mapOptional("ObjCSpaceAfterProperty", Style.ObjCSpaceAfterProperty);
index e8b737f..df74937 100644 (file)
@@ -71,6 +71,7 @@ namespace format {
   TYPE(LineComment)                                                            \
   TYPE(MacroBlockBegin)                                                        \
   TYPE(MacroBlockEnd)                                                          \
+  TYPE(NamespaceMacro)                                                         \
   TYPE(ObjCBlockLBrace)                                                        \
   TYPE(ObjCBlockLParen)                                                        \
   TYPE(ObjCDecl)                                                               \
@@ -531,8 +532,10 @@ struct FormatToken {
     // Detect "(inline|export)? namespace" in the beginning of a line.
     if (NamespaceTok && NamespaceTok->isOneOf(tok::kw_inline, tok::kw_export))
       NamespaceTok = NamespaceTok->getNextNonComment();
-    return NamespaceTok && NamespaceTok->is(tok::kw_namespace) ? NamespaceTok
-                                                               : nullptr;
+    return NamespaceTok &&
+                   NamespaceTok->isOneOf(tok::kw_namespace, TT_NamespaceMacro)
+               ? NamespaceTok
+               : nullptr;
   }
 
 private:
index 4438756..009b884 100644 (file)
@@ -41,6 +41,8 @@ FormatTokenLexer::FormatTokenLexer(const SourceManager &SourceMgr, FileID ID,
     Macros.insert({&IdentTable.get(StatementMacro), TT_StatementMacro});
   for (const std::string &TypenameMacro : Style.TypenameMacros)
     Macros.insert({&IdentTable.get(TypenameMacro), TT_TypenameMacro});
+  for (const std::string &NamespaceMacro : Style.NamespaceMacros)
+    Macros.insert({&IdentTable.get(NamespaceMacro), TT_NamespaceMacro});
 }
 
 ArrayRef<FormatToken *> FormatTokenLexer::lex() {
index 5354200..d04fc8f 100644 (file)
@@ -29,24 +29,41 @@ static const int kShortNamespaceMaxLines = 1;
 // 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;
+  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->getNextNonComment();
+    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 += ")";
   if (AddNewline)
     text += '\n';
   return text;
@@ -56,7 +73,8 @@ bool hasEndComment(const FormatToken *RBraceTok) {
   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;
 
@@ -66,19 +84,32 @@ bool validEndComment(const FormatToken *RBraceTok, StringRef NamespaceName) {
       new llvm::Regex("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *"
                       "namespace( +([a-zA-Z0-9:_]+))?\\.? *(\\*/)?$",
                       llvm::Regex::IgnoreCase);
-  SmallVector<StringRef, 7> Groups;
-  if (NamespaceCommentPattern->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())
+  static llvm::Regex *const NamespaceMacroCommentPattern =
+      new llvm::Regex("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *"
+                      "([a-zA-Z0-9_]+)\\(([a-zA-Z0-9:_]*)\\)\\.? *(\\*/)?$",
+                      llvm::Regex::IgnoreCase);
+
+  SmallVector<StringRef, 8> Groups;
+  if (NamespaceTok->is(TT_NamespaceMacro) &&
+      NamespaceMacroCommentPattern->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) ||
+             !NamespaceCommentPattern->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,
@@ -127,6 +158,13 @@ getNamespaceToken(const AnnotatedLine *Line,
   return NamespaceTok->getNamespaceToken();
 }
 
+StringRef
+getNamespaceTokenText(const AnnotatedLine *Line,
+                      const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) {
+  const FormatToken *NamespaceTok = getNamespaceToken(Line, AnnotatedLines);
+  return NamespaceTok ? NamespaceTok->TokenText : StringRef();
+}
+
 NamespaceEndCommentsFixer::NamespaceEndCommentsFixer(const Environment &Env,
                                                      const FormatStyle &Style)
     : TokenAnalyzer(Env, Style) {}
@@ -139,6 +177,7 @@ std::pair<tooling::Replacements, unsigned> NamespaceEndCommentsFixer::analyze(
   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];
@@ -160,8 +199,11 @@ std::pair<tooling::Replacements, unsigned> NamespaceEndCommentsFixer::analyze(
       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) {
@@ -189,12 +231,13 @@ std::pair<tooling::Replacements, unsigned> NamespaceEndCommentsFixer::analyze(
                       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 1dca764..6b698e2 100644 (file)
@@ -1194,12 +1194,12 @@ private:
 
     // Reset token type in case we have already looked at it and then
     // recovered from an error (e.g. failure to find the matching >).
-    if (!CurrentToken->isOneOf(TT_LambdaLSquare, TT_LambdaLBrace,
-                               TT_ForEachMacro, TT_TypenameMacro,
-                               TT_FunctionLBrace, TT_ImplicitStringLiteral,
-                               TT_InlineASMBrace, TT_JsFatArrow, TT_LambdaArrow,
-                               TT_OverloadedOperator, TT_RegexLiteral,
-                               TT_TemplateString, TT_ObjCStringLiteral))
+    if (!CurrentToken->isOneOf(
+            TT_LambdaLSquare, TT_LambdaLBrace, TT_ForEachMacro,
+            TT_TypenameMacro, TT_FunctionLBrace, TT_ImplicitStringLiteral,
+            TT_InlineASMBrace, TT_JsFatArrow, TT_LambdaArrow, TT_NamespaceMacro,
+            TT_OverloadedOperator, TT_RegexLiteral, TT_TemplateString,
+            TT_ObjCStringLiteral))
       CurrentToken->Type = TT_Unknown;
     CurrentToken->Role.reset();
     CurrentToken->MatchingParen = nullptr;
index d21df49..702ac6c 100644 (file)
@@ -115,6 +115,7 @@ public:
   /// \c true if this line starts a namespace definition.
   bool startsWithNamespace() const {
     return startsWith(tok::kw_namespace) ||
+           startsWith(TT_NamespaceMacro) ||
            startsWith(tok::kw_inline, tok::kw_namespace) ||
            startsWith(tok::kw_export, tok::kw_namespace);
   }
index 4e633c2..36a18de 100644 (file)
@@ -134,20 +134,29 @@ private:
   unsigned Indent = 0;
 };
 
-bool isNamespaceDeclaration(const AnnotatedLine *Line) {
-  const FormatToken *NamespaceTok = Line->First;
-  return NamespaceTok && NamespaceTok->getNamespaceToken();
-}
-
-bool isEndOfNamespace(const AnnotatedLine *Line,
-                      const SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) {
+const FormatToken *getMatchingNamespaceToken(
+    const AnnotatedLine *Line,
+    const SmallVectorImpl<AnnotatedLine *> &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<AnnotatedLine *> &AnnotatedLines) {
+  const FormatToken *NamespaceToken =
+      getMatchingNamespaceToken(Line, AnnotatedLines);
+  return NamespaceToken ? NamespaceToken->TokenText : StringRef();
 }
 
 class LineJoiner {
@@ -249,10 +258,11 @@ private:
          TheLine->Level != 0);
 
     if (Style.CompactNamespaces) {
-      if (isNamespaceDeclaration(TheLine)) {
+      if (auto nsToken = TheLine->First->getNamespaceToken()) {
         int i = 0;
         unsigned closingLine = TheLine->MatchingClosingBlockLineIndex - 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]->MatchingClosingBlockLineIndex &&
                I[i + 1]->Last->TotalLength < Limit;
              i++, closingLine--) {
@@ -264,10 +274,12 @@ private:
         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
index 7acf33a..a35e98a 100644 (file)
@@ -630,7 +630,7 @@ static bool isIIFE(const UnwrappedLine &Line,
 
 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;
@@ -1122,6 +1122,10 @@ void UnwrappedLineParser::parseStructuralElement() {
       parseStatementMacro();
       return;
     }
+    if (Style.isCpp() && FormatTok->is(TT_NamespaceMacro)) {
+      parseNamespace();
+      return;
+    }
     // In all other cases, parse the declaration.
     break;
   default:
@@ -1860,12 +1864,17 @@ void UnwrappedLineParser::parseTryCatch() {
 }
 
 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 87405bc..885aac0 100644 (file)
@@ -1870,9 +1870,117 @@ TEST_F(FormatTest, FormatsNamespaces) {
                    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",
@@ -11700,6 +11808,12 @@ TEST_F(FormatTest, ParsesConfiguration) {
   CHECK_PARSE("StatementMacros: [QUNUSED, QT_REQUIRE_VERSION]", StatementMacros,
               std::vector<std::string>({"QUNUSED", "QT_REQUIRE_VERSION"}));
 
+  Style.NamespaceMacros.clear();
+  CHECK_PARSE("NamespaceMacros: [TESTSUITE]", NamespaceMacros,
+              std::vector<std::string>{"TESTSUITE"});
+  CHECK_PARSE("NamespaceMacros: [TESTSUITE, SUITE]", NamespaceMacros,
+              std::vector<std::string>({"TESTSUITE", "SUITE"}));
+
   Style.IncludeStyle.IncludeCategories.clear();
   std::vector<tooling::IncludeStyle::IncludeCategory> ExpectedCategories = {
       {"abc/.*", 2}, {".*", 1}};
index d4c16c8..44cb4ef 100644 (file)
@@ -52,6 +52,7 @@ TEST_F(NamespaceEndCommentsFixerTest, AddsEndComment) {
                                     "int i;\n"
                                     "int j;\n"
                                     "}"));
+
   EXPECT_EQ("namespace {\n"
             "int i;\n"
             "int j;\n"
@@ -248,6 +249,85 @@ TEST_F(NamespaceEndCommentsFixerTest, AddsEndComment) {
                                     "// 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"
@@ -380,6 +460,54 @@ TEST_F(NamespaceEndCommentsFixerTest, KeepsValidEndComment) {
                                     "}; /* 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"
@@ -446,6 +574,96 @@ TEST_F(NamespaceEndCommentsFixerTest, UpdatesInvalidEndLineComment) {
                                     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 @@ TEST_F(NamespaceEndCommentsFixerTest, UpdatesInvalidEndBlockComment) {
             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"