浏览代码

clang-format: Fix formatting C++ namespaces with preceding 'inline' or 'export' specifier

This fixes formatting namespaces with preceding 'inline' and 'export' (Modules TS) specifiers.

This change fixes namespaces not being identified as such with preceding 'inline' or 'export' specifiers.

Motivation: I was experimenting with the Modules TS (-fmodules-ts) and found it would be useful if clang-format would correctly format 'export namespace'. While making the changes, I noticed that similar issues still exist with 'inline namespace', and addressed them as well.

Patch by Marco Elver!

Reviewers: klimek, djasper, owenpan, sammccall

Reviewed By: owenpan, sammccall

Subscribers: owenpan, cfe-commits

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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@341450 91177308-0d34-0410-b5e6-96231b3b80d8
Sam McCall 7 年之前
父节点
当前提交
024b9a4311

+ 2 - 5
lib/Format/Format.cpp

@@ -1309,8 +1309,7 @@ private:
     std::set<unsigned> DeletedLines;
     std::set<unsigned> DeletedLines;
     for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
     for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) {
       auto &Line = *AnnotatedLines[i];
       auto &Line = *AnnotatedLines[i];
-      if (Line.startsWith(tok::kw_namespace) ||
-          Line.startsWith(tok::kw_inline, tok::kw_namespace)) {
+      if (Line.startsWithNamespace()) {
         checkEmptyNamespace(AnnotatedLines, i, i, DeletedLines);
         checkEmptyNamespace(AnnotatedLines, i, i, DeletedLines);
       }
       }
     }
     }
@@ -1347,9 +1346,7 @@ private:
       if (AnnotatedLines[CurrentLine]->startsWith(tok::r_brace))
       if (AnnotatedLines[CurrentLine]->startsWith(tok::r_brace))
         break;
         break;
 
 
-      if (AnnotatedLines[CurrentLine]->startsWith(tok::kw_namespace) ||
-          AnnotatedLines[CurrentLine]->startsWith(tok::kw_inline,
-                                                  tok::kw_namespace)) {
+      if (AnnotatedLines[CurrentLine]->startsWithNamespace()) {
         if (!checkEmptyNamespace(AnnotatedLines, CurrentLine, NewLine,
         if (!checkEmptyNamespace(AnnotatedLines, CurrentLine, NewLine,
                                  DeletedLines))
                                  DeletedLines))
           return false;
           return false;

+ 2 - 2
lib/Format/FormatToken.h

@@ -520,8 +520,8 @@ struct FormatToken {
     const FormatToken *NamespaceTok = this;
     const FormatToken *NamespaceTok = this;
     if (is(tok::comment))
     if (is(tok::comment))
       NamespaceTok = NamespaceTok->getNextNonComment();
       NamespaceTok = NamespaceTok->getNextNonComment();
-    // Detect "(inline)? namespace" in the beginning of a line.
-    if (NamespaceTok && NamespaceTok->is(tok::kw_inline))
+    // Detect "(inline|export)? namespace" in the beginning of a line.
+    if (NamespaceTok && NamespaceTok->isOneOf(tok::kw_inline, tok::kw_export))
       NamespaceTok = NamespaceTok->getNextNonComment();
       NamespaceTok = NamespaceTok->getNextNonComment();
     return NamespaceTok && NamespaceTok->is(tok::kw_namespace) ? NamespaceTok
     return NamespaceTok && NamespaceTok->is(tok::kw_namespace) ? NamespaceTok
                                                                : nullptr;
                                                                : nullptr;

+ 1 - 6
lib/Format/NamespaceEndCommentsFixer.cpp

@@ -125,12 +125,7 @@ getNamespaceToken(const AnnotatedLine *Line,
     if (StartLineIndex > 0)
     if (StartLineIndex > 0)
       NamespaceTok = AnnotatedLines[StartLineIndex - 1]->First;
       NamespaceTok = AnnotatedLines[StartLineIndex - 1]->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();
 }
 }
 
 
 NamespaceEndCommentsFixer::NamespaceEndCommentsFixer(const Environment &Env,
 NamespaceEndCommentsFixer::NamespaceEndCommentsFixer(const Environment &Env,

+ 7 - 0
lib/Format/TokenAnnotator.h

@@ -105,6 +105,13 @@ public:
     return !Last->isOneOf(tok::semi, tok::comment);
     return !Last->isOneOf(tok::semi, tok::comment);
   }
   }
 
 
+  /// \c true if this line starts a namespace definition.
+  bool startsWithNamespace() const {
+    return startsWith(tok::kw_namespace) ||
+           startsWith(tok::kw_inline, tok::kw_namespace) ||
+           startsWith(tok::kw_export, tok::kw_namespace);
+  }
+
   FormatToken *First;
   FormatToken *First;
   FormatToken *Last;
   FormatToken *Last;
 
 

+ 2 - 2
lib/Format/UnwrappedLineFormatter.cpp

@@ -535,7 +535,7 @@ private:
         Tok->SpacesRequiredBefore = 0;
         Tok->SpacesRequiredBefore = 0;
         Tok->CanBreakBefore = true;
         Tok->CanBreakBefore = true;
         return 1;
         return 1;
-      } else if (Limit != 0 && !Line.startsWith(tok::kw_namespace) &&
+      } else if (Limit != 0 && !Line.startsWithNamespace() &&
                  !startsExternCBlock(Line)) {
                  !startsExternCBlock(Line)) {
         // We don't merge short records.
         // We don't merge short records.
         FormatToken *RecordTok = Line.First;
         FormatToken *RecordTok = Line.First;
@@ -1160,7 +1160,7 @@ void UnwrappedLineFormatter::formatFirstToken(
   // Remove empty lines after "{".
   // Remove empty lines after "{".
   if (!Style.KeepEmptyLinesAtTheStartOfBlocks && PreviousLine &&
   if (!Style.KeepEmptyLinesAtTheStartOfBlocks && PreviousLine &&
       PreviousLine->Last->is(tok::l_brace) &&
       PreviousLine->Last->is(tok::l_brace) &&
-      PreviousLine->First->isNot(tok::kw_namespace) &&
+      !PreviousLine->startsWithNamespace() &&
       !startsExternCBlock(*PreviousLine))
       !startsExternCBlock(*PreviousLine))
     Newlines = 1;
     Newlines = 1;
 
 

+ 10 - 7
lib/Format/UnwrappedLineParser.cpp

@@ -992,13 +992,6 @@ void UnwrappedLineParser::parseStructuralElement() {
   case tok::kw_namespace:
   case tok::kw_namespace:
     parseNamespace();
     parseNamespace();
     return;
     return;
-  case tok::kw_inline:
-    nextToken();
-    if (FormatTok->Tok.is(tok::kw_namespace)) {
-      parseNamespace();
-      return;
-    }
-    break;
   case tok::kw_public:
   case tok::kw_public:
   case tok::kw_protected:
   case tok::kw_protected:
   case tok::kw_private:
   case tok::kw_private:
@@ -1066,6 +1059,16 @@ void UnwrappedLineParser::parseStructuralElement() {
       parseJavaScriptEs6ImportExport();
       parseJavaScriptEs6ImportExport();
       return;
       return;
     }
     }
+    if (!Style.isCpp())
+      break;
+    // Handle C++ "(inline|export) namespace".
+    LLVM_FALLTHROUGH;
+  case tok::kw_inline:
+    nextToken();
+    if (FormatTok->Tok.is(tok::kw_namespace)) {
+      parseNamespace();
+      return;
+    }
     break;
     break;
   case tok::identifier:
   case tok::identifier:
     if (FormatTok->is(TT_ForEachMacro)) {
     if (FormatTok->is(TT_ForEachMacro)) {

+ 75 - 0
unittests/Format/FormatTest.cpp

@@ -200,6 +200,42 @@ TEST_F(FormatTest, RemovesEmptyLines) {
                    "int    i;\n"
                    "int    i;\n"
                    "}",
                    "}",
                    getGoogleStyle()));
                    getGoogleStyle()));
+  EXPECT_EQ("/* something */ namespace N {\n"
+            "\n"
+            "int i;\n"
+            "}",
+            format("/* something */ namespace N {\n"
+                   "\n"
+                   "int    i;\n"
+                   "}",
+                   getGoogleStyle()));
+  EXPECT_EQ("inline namespace N {\n"
+            "\n"
+            "int i;\n"
+            "}",
+            format("inline namespace N {\n"
+                   "\n"
+                   "int    i;\n"
+                   "}",
+                   getGoogleStyle()));
+  EXPECT_EQ("/* something */ inline namespace N {\n"
+            "\n"
+            "int i;\n"
+            "}",
+            format("/* something */ inline namespace N {\n"
+                   "\n"
+                   "int    i;\n"
+                   "}",
+                   getGoogleStyle()));
+  EXPECT_EQ("export namespace N {\n"
+            "\n"
+            "int i;\n"
+            "}",
+            format("export namespace N {\n"
+                   "\n"
+                   "int    i;\n"
+                   "}",
+                   getGoogleStyle()));
   EXPECT_EQ("extern /**/ \"C\" /**/ {\n"
   EXPECT_EQ("extern /**/ \"C\" /**/ {\n"
             "\n"
             "\n"
             "int i;\n"
             "int i;\n"
@@ -1220,12 +1256,25 @@ TEST_F(FormatTest, UnderstandsAccessSpecifiers) {
                "private:\n"
                "private:\n"
                "  void f() {}\n"
                "  void f() {}\n"
                "};");
                "};");
+  verifyFormat("export class A {\n"
+               "public:\n"
+               "public: // comment\n"
+               "protected:\n"
+               "private:\n"
+               "  void f() {}\n"
+               "};");
   verifyGoogleFormat("class A {\n"
   verifyGoogleFormat("class A {\n"
                      " public:\n"
                      " public:\n"
                      " protected:\n"
                      " protected:\n"
                      " private:\n"
                      " private:\n"
                      "  void f() {}\n"
                      "  void f() {}\n"
                      "};");
                      "};");
+  verifyGoogleFormat("export class A {\n"
+                     " public:\n"
+                     " protected:\n"
+                     " private:\n"
+                     "  void f() {}\n"
+                     "};");
   verifyFormat("class A {\n"
   verifyFormat("class A {\n"
                "public slots:\n"
                "public slots:\n"
                "  void f1() {}\n"
                "  void f1() {}\n"
@@ -1597,16 +1646,36 @@ TEST_F(FormatTest, FormatsNamespaces) {
                "void f() { f(); }\n"
                "void f() { f(); }\n"
                "}",
                "}",
                LLVMWithNoNamespaceFix);
                LLVMWithNoNamespaceFix);
+  verifyFormat("/* something */ namespace some_namespace {\n"
+               "class A {};\n"
+               "void f() { f(); }\n"
+               "}",
+               LLVMWithNoNamespaceFix);
   verifyFormat("namespace {\n"
   verifyFormat("namespace {\n"
                "class A {};\n"
                "class A {};\n"
                "void f() { f(); }\n"
                "void f() { f(); }\n"
                "}",
                "}",
                LLVMWithNoNamespaceFix);
                LLVMWithNoNamespaceFix);
+  verifyFormat("/* something */ namespace {\n"
+               "class A {};\n"
+               "void f() { f(); }\n"
+               "}",
+               LLVMWithNoNamespaceFix);
   verifyFormat("inline namespace X {\n"
   verifyFormat("inline namespace X {\n"
                "class A {};\n"
                "class A {};\n"
                "void f() { f(); }\n"
                "void f() { f(); }\n"
                "}",
                "}",
                LLVMWithNoNamespaceFix);
                LLVMWithNoNamespaceFix);
+  verifyFormat("/* something */ inline namespace X {\n"
+               "class A {};\n"
+               "void f() { f(); }\n"
+               "}",
+               LLVMWithNoNamespaceFix);
+  verifyFormat("export namespace X {\n"
+               "class A {};\n"
+               "void f() { f(); }\n"
+               "}",
+               LLVMWithNoNamespaceFix);
   verifyFormat("using namespace some_namespace;\n"
   verifyFormat("using namespace some_namespace;\n"
                "class A {};\n"
                "class A {};\n"
                "void f() { f(); }",
                "void f() { f(); }",
@@ -7602,6 +7671,12 @@ TEST_F(FormatTest, SplitEmptyNamespace) {
   verifyFormat("inline namespace Foo\n"
   verifyFormat("inline namespace Foo\n"
                "{};",
                "{};",
                Style);
                Style);
+  verifyFormat("/* something */ inline namespace Foo\n"
+               "{};",
+               Style);
+  verifyFormat("export namespace Foo\n"
+               "{};",
+               Style);
   verifyFormat("namespace Foo\n"
   verifyFormat("namespace Foo\n"
                "{\n"
                "{\n"
                "void Bar();\n"
                "void Bar();\n"