zequanwu updated this revision to Diff 414222.
zequanwu marked 2 inline comments as done.
zequanwu added a comment.

Refactor.
Add a test for `namespace A __attribute__((availability(macos, 
introduced=10.15))) {`.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D121269/new/

https://reviews.llvm.org/D121269

Files:
  clang/lib/Format/NamespaceEndCommentsFixer.cpp
  clang/unittests/Format/NamespaceEndCommentsFixerTest.cpp

Index: clang/unittests/Format/NamespaceEndCommentsFixerTest.cpp
===================================================================
--- clang/unittests/Format/NamespaceEndCommentsFixerTest.cpp
+++ clang/unittests/Format/NamespaceEndCommentsFixerTest.cpp
@@ -189,6 +189,49 @@
                 "int i;\n"
                 "int j;\n"
                 "}"));
+  EXPECT_EQ("#define M(x) x##x\n"
+            "namespace A M(x) {\n"
+            "int i;\n"
+            "int j;\n"
+            "}// namespace A M(x)",
+            fixNamespaceEndComments("#define M(x) x##x\n"
+                                    "namespace A M(x) {\n"
+                                    "int i;\n"
+                                    "int j;\n"
+                                    "}"));
+  EXPECT_EQ(
+      "#define B __attribute__((availability(macos, introduced=10.15)))\n"
+      "namespace A B {\n"
+      "int i;\n"
+      "int j;\n"
+      "}// namespace A B",
+      fixNamespaceEndComments(
+          "#define B __attribute__((availability(macos, introduced=10.15)))\n"
+          "namespace A B {\n"
+          "int i;\n"
+          "int j;\n"
+          "}"));
+  EXPECT_EQ("#define M(x) x##x\n"
+            "namespace A::B M(x) {\n"
+            "int i;\n"
+            "int j;\n"
+            "}// namespace A::B",
+            fixNamespaceEndComments("#define M(x) x##x\n"
+                                    "namespace A::B M(x) {\n"
+                                    "int i;\n"
+                                    "int j;\n"
+                                    "}"));
+  EXPECT_EQ(
+      "namespace A __attribute__((availability(macos, introduced=10.15))) {\n"
+      "int i;\n"
+      "int j;\n"
+      "}// namespace A",
+      fixNamespaceEndComments(
+          "namespace A __attribute__((availability(macos, introduced=10.15))) "
+          "{\n"
+          "int i;\n"
+          "int j;\n"
+          "}"));
   EXPECT_EQ("inline namespace A {\n"
             "int i;\n"
             "int j;\n"
Index: clang/lib/Format/NamespaceEndCommentsFixer.cpp
===================================================================
--- clang/lib/Format/NamespaceEndCommentsFixer.cpp
+++ clang/lib/Format/NamespaceEndCommentsFixer.cpp
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "NamespaceEndCommentsFixer.h"
+#include "clang/Basic/TokenKinds.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/Regex.h"
 
@@ -22,6 +23,39 @@
 namespace format {
 
 namespace {
+// Iterates all tokens starting from StartTok to EndTok and apply Fn to all
+// tokens between them including StartTok and EndTok. Returns the token after
+// EndTok.
+const FormatToken *
+processTokens(const FormatToken *Tok, tok::TokenKind StartTok,
+              tok::TokenKind EndTok,
+              llvm::function_ref<void(const FormatToken *)> Fn) {
+  if (!Tok || !Tok->is(StartTok))
+    return Tok;
+  int NestLevel = 0;
+  do {
+    if (Tok->is(StartTok))
+      ++NestLevel;
+    else if (Tok->is(EndTok))
+      --NestLevel;
+    if (Fn)
+      Fn(Tok);
+    Tok = Tok->getNextNonComment();
+  } while (Tok && NestLevel > 0);
+  return Tok;
+}
+
+const FormatToken *skipAttribute(const FormatToken *Tok) {
+  if (!Tok)
+    return nullptr;
+  if (Tok->is(tok::kw___attribute)) {
+    Tok = Tok->getNextNonComment();
+    Tok = processTokens(Tok, tok::l_paren, tok::r_paren, nullptr);
+  } else if (Tok->is(tok::l_square))
+    Tok = processTokens(Tok, tok::l_square, tok::r_square, nullptr);
+  return Tok;
+}
+
 // Computes the name of a namespace given the namespace token.
 // Returns "" for anonymous namespace.
 std::string computeName(const FormatToken *NamespaceTok) {
@@ -41,44 +75,67 @@
     }
   } else {
     // Skip attributes.
-    if (Tok && Tok->is(tok::l_square)) {
-      for (int NestLevel = 1; NestLevel > 0;) {
-        Tok = Tok->getNextNonComment();
-        if (!Tok)
-          break;
-        if (Tok->is(tok::l_square))
-          ++NestLevel;
-        else if (Tok->is(tok::r_square))
-          --NestLevel;
-      }
-      if (Tok)
-        Tok = Tok->getNextNonComment();
-    }
+    Tok = skipAttribute(Tok);
 
-    // Use the string after `namespace` as a name candidate until `{` or `::` or
-    // `(`. If the name is empty, use the candicate.
     std::string FirstNSName;
     // For `namespace [[foo]] A::B::inline C {` or
     // `namespace MACRO1 MACRO2 A::B::inline C {`, returns "A::B::inline C".
     // Peek for the first '::' (or '{' or '(')) and then return all tokens from
     // one token before that up until the '{'. A '(' might be a macro with
     // arguments.
-    const FormatToken *FirstNSTok = Tok;
+    const FormatToken *FirstNSTok = nullptr;
     while (Tok && !Tok->isOneOf(tok::l_brace, tok::coloncolon, tok::l_paren)) {
-      FirstNSName += FirstNSTok->TokenText;
+      if (FirstNSTok)
+        FirstNSName += FirstNSTok->TokenText;
       FirstNSTok = Tok;
       Tok = Tok->getNextNonComment();
     }
 
-    Tok = FirstNSTok;
+    if (FirstNSTok)
+      Tok = FirstNSTok;
+    Tok = skipAttribute(Tok);
+
+    FirstNSTok = nullptr;
+    // Add everything from '(' to ')'.
+    auto AddToken = [&name](const FormatToken *Tok) { name += Tok->TokenText; };
+    bool IsPrevColoncolon = false;
+    bool HasColoncolon = false;
+    bool IsPrevInline = false;
+    bool NameFinished = false;
+    // If we found '::' in name, then it's the name. Otherwise, we can't tell
+    // which one is name. For example, `namespace A B {`.
     while (Tok && !Tok->is(tok::l_brace)) {
-      name += Tok->TokenText;
-      if (Tok->is(tok::kw_inline))
-        name += " ";
+      if (FirstNSTok) {
+        if (!IsPrevInline && HasColoncolon && !IsPrevColoncolon) {
+          if (FirstNSTok->is(tok::l_paren)) {
+            FirstNSTok = Tok =
+                processTokens(FirstNSTok, tok::l_paren, tok::r_paren, AddToken);
+            continue;
+          }
+          if (!FirstNSTok->is(tok::coloncolon)) {
+            NameFinished = true;
+            break;
+          }
+        }
+        name += FirstNSTok->TokenText;
+        IsPrevColoncolon = FirstNSTok->is(tok::coloncolon);
+        HasColoncolon |= IsPrevColoncolon;
+        if (FirstNSTok->is(tok::kw_inline)) {
+          name += " ";
+          IsPrevInline = true;
+        }
+      }
+      FirstNSTok = Tok;
       Tok = Tok->getNextNonComment();
+      // Skip attribute
+      const FormatToken *TokAfterAttr = skipAttribute(Tok);
+      if (TokAfterAttr != Tok)
+        FirstNSTok = Tok = TokAfterAttr;
     }
-    if (name.empty())
-      name = FirstNSName;
+    if (!NameFinished && FirstNSTok && !FirstNSTok->is(tok::l_brace))
+      name += FirstNSTok->TokenText;
+    if (!FirstNSName.empty() && !HasColoncolon)
+      name = FirstNSName + (!name.empty() ? " " + name : "");
   }
   return name;
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to