r296341 - [clang-format] Add a NamespaceEndCommentsFixer

Krasimir Georgiev via cfe-commits cfe-commits at lists.llvm.org
Mon Feb 27 05:28:37 PST 2017


Author: krasimir
Date: Mon Feb 27 07:28:36 2017
New Revision: 296341

URL: http://llvm.org/viewvc/llvm-project?rev=296341&view=rev
Log:
[clang-format] Add a NamespaceEndCommentsFixer

Summary:
This patch adds a NamespaceEndCommentsFixer TokenAnalyzer for clang-format,
which fixes end namespace comments.
It currently supports inserting and updating existing wrong comments.

Example source:
```
namespace A {
int i;
}

namespace B {
int j;
} // namespace A
```

after formatting:
```
namespace A {
int i;
} // namespace A

namespace B {
int j;
} // namespace B
```

Reviewers: klimek, djasper

Reviewed By: djasper

Subscribers: klimek, mgorny

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

Added:
    cfe/trunk/lib/Format/NamespaceEndCommentsFixer.cpp
    cfe/trunk/lib/Format/NamespaceEndCommentsFixer.h
    cfe/trunk/unittests/Format/NamespaceEndCommentsFixerTest.cpp
Modified:
    cfe/trunk/include/clang/Format/Format.h
    cfe/trunk/lib/Format/CMakeLists.txt
    cfe/trunk/lib/Format/Format.cpp
    cfe/trunk/lib/Format/TokenAnnotator.h
    cfe/trunk/lib/Format/UnwrappedLineParser.cpp
    cfe/trunk/lib/Format/UnwrappedLineParser.h
    cfe/trunk/unittests/Format/CMakeLists.txt

Modified: cfe/trunk/include/clang/Format/Format.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Format/Format.h?rev=296341&r1=296340&r2=296341&view=diff
==============================================================================
--- cfe/trunk/include/clang/Format/Format.h (original)
+++ cfe/trunk/include/clang/Format/Format.h Mon Feb 27 07:28:36 2017
@@ -829,6 +829,15 @@ tooling::Replacements cleanup(const Form
                               ArrayRef<tooling::Range> Ranges,
                               StringRef FileName = "<stdin>");
 
+/// \brief Fix namespace end comments in the given \p Ranges in \p Code.
+///
+/// Returns the ``Replacements`` that fix the namespace comments in all
+/// \p Ranges in \p Code.
+tooling::Replacements fixNamespaceEndComments(const FormatStyle &Style,
+                                              StringRef Code,
+                                              ArrayRef<tooling::Range> Ranges,
+                                              StringRef FileName = "<stdin>");
+
 /// \brief Returns the ``LangOpts`` that the formatter expects you to set.
 ///
 /// \param Style determines specific settings for lexing mode.

Modified: cfe/trunk/lib/Format/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/CMakeLists.txt?rev=296341&r1=296340&r2=296341&view=diff
==============================================================================
--- cfe/trunk/lib/Format/CMakeLists.txt (original)
+++ cfe/trunk/lib/Format/CMakeLists.txt Mon Feb 27 07:28:36 2017
@@ -7,6 +7,7 @@ add_clang_library(clangFormat
   Format.cpp
   FormatToken.cpp
   FormatTokenLexer.cpp
+  NamespaceEndCommentsFixer.cpp
   SortJavaScriptImports.cpp
   TokenAnalyzer.cpp
   TokenAnnotator.cpp

Modified: cfe/trunk/lib/Format/Format.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/Format.cpp?rev=296341&r1=296340&r2=296341&view=diff
==============================================================================
--- cfe/trunk/lib/Format/Format.cpp (original)
+++ cfe/trunk/lib/Format/Format.cpp Mon Feb 27 07:28:36 2017
@@ -17,6 +17,7 @@
 #include "AffectedRangeManager.h"
 #include "ContinuationIndenter.h"
 #include "FormatTokenLexer.h"
+#include "NamespaceEndCommentsFixer.h"
 #include "SortJavaScriptImports.h"
 #include "TokenAnalyzer.h"
 #include "TokenAnnotator.h"
@@ -1857,6 +1858,16 @@ tooling::Replacements cleanup(const Form
   return Clean.process();
 }
 
+tooling::Replacements fixNamespaceEndComments(const FormatStyle &Style,
+                                              StringRef Code,
+                                              ArrayRef<tooling::Range> Ranges,
+                                              StringRef FileName) {
+  std::unique_ptr<Environment> Env =
+      Environment::CreateVirtualEnvironment(Code, FileName, Ranges);
+  NamespaceEndCommentsFixer Fix(*Env, Style);
+  return Fix.process();
+}
+
 LangOptions getFormattingLangOpts(const FormatStyle &Style) {
   LangOptions LangOpts;
   LangOpts.CPlusPlus = 1;

Added: cfe/trunk/lib/Format/NamespaceEndCommentsFixer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/NamespaceEndCommentsFixer.cpp?rev=296341&view=auto
==============================================================================
--- cfe/trunk/lib/Format/NamespaceEndCommentsFixer.cpp (added)
+++ cfe/trunk/lib/Format/NamespaceEndCommentsFixer.cpp Mon Feb 27 07:28:36 2017
@@ -0,0 +1,165 @@
+//===--- NamespaceEndCommentsFixer.cpp --------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file implements NamespaceEndCommentsFixer, a TokenAnalyzer that
+/// fixes namespace end comments.
+///
+//===----------------------------------------------------------------------===//
+
+#include "NamespaceEndCommentsFixer.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Regex.h"
+
+#define DEBUG_TYPE "namespace-end-comments-fixer"
+
+namespace clang {
+namespace format {
+
+namespace {
+// The maximal number of lines that a short namespace spans.
+// Short namespaces don't need an end comment.
+static const int kShortNamespaceMaxLines = 1;
+
+// Matches a valid namespace end comment.
+// Valid namespace end comments don't need to be edited.
+static llvm::Regex kNamespaceCommentPattern =
+    llvm::Regex("^/[/*] *(end (of )?)? *(anonymous|unnamed)? *"
+                "namespace( +([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) &&
+         "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();
+  }
+  return name;
+}
+
+std::string computeEndCommentText(StringRef NamespaceName, bool AddNewline) {
+  std::string text = "// namespace";
+  if (!NamespaceName.empty()) {
+    text += ' ';
+    text += NamespaceName;
+  }
+  if (AddNewline)
+    text += '\n';
+  return text;
+}
+
+bool isShort(const FormatToken *NamespaceTok, const FormatToken *RBraceTok,
+             const SourceManager &SourceMgr) {
+  int StartLine =
+      SourceMgr.getSpellingLineNumber(NamespaceTok->Tok.getLocation());
+  int EndLine = SourceMgr.getSpellingLineNumber(RBraceTok->Tok.getLocation());
+  return EndLine - StartLine + 1 <= kShortNamespaceMaxLines;
+}
+
+bool hasEndComment(const FormatToken *RBraceTok) {
+  return RBraceTok->Next && RBraceTok->Next->is(tok::comment);
+}
+
+bool validEndComment(const FormatToken *RBraceTok, StringRef NamespaceName) {
+  assert(hasEndComment(RBraceTok));
+  const FormatToken *Comment = RBraceTok->Next;
+  SmallVector<StringRef, 7> 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())
+      return false;
+    return NamespaceNameInComment == NamespaceName;
+  }
+  return false;
+}
+
+void addEndComment(const FormatToken *RBraceTok, StringRef EndCommentText,
+                   const SourceManager &SourceMgr,
+                   tooling::Replacements *Fixes) {
+  auto EndLoc = RBraceTok->Tok.getEndLoc();
+  auto Range = CharSourceRange::getCharRange(EndLoc, EndLoc);
+  auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, EndCommentText));
+  if (Err) {
+    llvm::errs() << "Error while adding namespace end comment: "
+                 << llvm::toString(std::move(Err)) << "\n";
+  }
+}
+
+void updateEndComment(const FormatToken *RBraceTok, StringRef EndCommentText,
+                      const SourceManager &SourceMgr,
+                      tooling::Replacements *Fixes) {
+  assert(hasEndComment(RBraceTok));
+  const FormatToken *Comment = RBraceTok->Next;
+  auto Range = CharSourceRange::getCharRange(Comment->getStartOfNonWhitespace(),
+                                             Comment->Tok.getEndLoc());
+  auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, EndCommentText));
+  if (Err) {
+    llvm::errs() << "Error while updating namespace end comment: "
+                 << llvm::toString(std::move(Err)) << "\n";
+  }
+}
+} // namespace
+
+NamespaceEndCommentsFixer::NamespaceEndCommentsFixer(const Environment &Env,
+                                                     const FormatStyle &Style)
+    : TokenAnalyzer(Env, Style) {}
+
+tooling::Replacements NamespaceEndCommentsFixer::analyze(
+    TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
+    FormatTokenLexer &Tokens) {
+  const SourceManager &SourceMgr = Env.getSourceManager();
+  AffectedRangeMgr.computeAffectedLines(AnnotatedLines.begin(),
+                                        AnnotatedLines.end());
+  tooling::Replacements Fixes;
+  for (size_t I = 0, E = AnnotatedLines.size(); I != E; ++I) {
+    if (!AnnotatedLines[I]->Affected || AnnotatedLines[I]->InPPDirective ||
+        !AnnotatedLines[I]->startsWith(tok::r_brace))
+      continue;
+    const AnnotatedLine *EndLine = AnnotatedLines[I];
+    size_t StartLineIndex = EndLine->MatchingOpeningBlockLineIndex;
+    if (StartLineIndex == UnwrappedLine::kInvalidIndex)
+      continue;
+    assert(StartLineIndex < E);
+    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->isNot(tok::kw_namespace))
+      continue;
+    const FormatToken *RBraceTok = EndLine->First;
+    const std::string NamespaceName = computeName(NamespaceTok);
+    bool AddNewline = (I + 1 < E) &&
+                      AnnotatedLines[I + 1]->First->NewlinesBefore == 0 &&
+                      AnnotatedLines[I + 1]->First->isNot(tok::eof);
+    const std::string EndCommentText =
+        computeEndCommentText(NamespaceName, AddNewline);
+    if (!hasEndComment(RBraceTok)) {
+      if (!isShort(NamespaceTok, RBraceTok, SourceMgr))
+        addEndComment(RBraceTok, EndCommentText, SourceMgr, &Fixes);
+      continue;
+    }
+    if (!validEndComment(RBraceTok, NamespaceName))
+      updateEndComment(RBraceTok, EndCommentText, SourceMgr, &Fixes);
+  }
+  return Fixes;
+}
+
+} // namespace format
+} // namespace clang

Added: cfe/trunk/lib/Format/NamespaceEndCommentsFixer.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/NamespaceEndCommentsFixer.h?rev=296341&view=auto
==============================================================================
--- cfe/trunk/lib/Format/NamespaceEndCommentsFixer.h (added)
+++ cfe/trunk/lib/Format/NamespaceEndCommentsFixer.h Mon Feb 27 07:28:36 2017
@@ -0,0 +1,37 @@
+//===--- NamespaceEndCommentsFixer.h ----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file declares NamespaceEndCommentsFixer, a TokenAnalyzer that
+/// fixes namespace end comments.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_FORMAT_NAMESPACEENDCOMMENTSFIXER_H
+#define LLVM_CLANG_LIB_FORMAT_NAMESPACEENDCOMMENTSFIXER_H
+
+#include "TokenAnalyzer.h"
+
+namespace clang {
+namespace format {
+
+class NamespaceEndCommentsFixer : public TokenAnalyzer {
+public:
+  NamespaceEndCommentsFixer(const Environment &Env, const FormatStyle &Style);
+
+  tooling::Replacements
+  analyze(TokenAnnotator &Annotator,
+          SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
+          FormatTokenLexer &Tokens) override;
+};
+
+} // end namespace format
+} // end namespace clang
+
+#endif

Modified: cfe/trunk/lib/Format/TokenAnnotator.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/TokenAnnotator.h?rev=296341&r1=296340&r2=296341&view=diff
==============================================================================
--- cfe/trunk/lib/Format/TokenAnnotator.h (original)
+++ cfe/trunk/lib/Format/TokenAnnotator.h Mon Feb 27 07:28:36 2017
@@ -39,6 +39,7 @@ class AnnotatedLine {
 public:
   AnnotatedLine(const UnwrappedLine &Line)
       : First(Line.Tokens.front().Tok), Level(Line.Level),
+        MatchingOpeningBlockLineIndex(Line.MatchingOpeningBlockLineIndex),
         InPPDirective(Line.InPPDirective),
         MustBeDeclaration(Line.MustBeDeclaration), MightBeFunctionDecl(false),
         IsMultiVariableDeclStmt(false), Affected(false),
@@ -109,6 +110,7 @@ public:
 
   LineType Type;
   unsigned Level;
+  size_t MatchingOpeningBlockLineIndex;
   bool InPPDirective;
   bool MustBeDeclaration;
   bool MightBeFunctionDecl;

Modified: cfe/trunk/lib/Format/UnwrappedLineParser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/UnwrappedLineParser.cpp?rev=296341&r1=296340&r2=296341&view=diff
==============================================================================
--- cfe/trunk/lib/Format/UnwrappedLineParser.cpp (original)
+++ cfe/trunk/lib/Format/UnwrappedLineParser.cpp Mon Feb 27 07:28:36 2017
@@ -428,6 +428,8 @@ void UnwrappedLineParser::parseBlock(boo
     parseParens();
 
   addUnwrappedLine();
+  size_t OpeningLineIndex =
+      Lines.empty() ? (UnwrappedLine::kInvalidIndex) : (Lines.size() - 1);
 
   ScopedDeclarationState DeclarationState(*Line, DeclarationScopeStack,
                                           MustBeDeclaration);
@@ -453,6 +455,7 @@ void UnwrappedLineParser::parseBlock(boo
   if (MunchSemi && FormatTok->Tok.is(tok::semi))
     nextToken();
   Line->Level = InitialLevel;
+  Line->MatchingOpeningBlockLineIndex = OpeningLineIndex;
 }
 
 static bool isGoogScope(const UnwrappedLine &Line) {

Modified: cfe/trunk/lib/Format/UnwrappedLineParser.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/UnwrappedLineParser.h?rev=296341&r1=296340&r2=296341&view=diff
==============================================================================
--- cfe/trunk/lib/Format/UnwrappedLineParser.h (original)
+++ cfe/trunk/lib/Format/UnwrappedLineParser.h Mon Feb 27 07:28:36 2017
@@ -48,6 +48,14 @@ struct UnwrappedLine {
   bool InPPDirective;
 
   bool MustBeDeclaration;
+
+  /// \brief If this \c UnwrappedLine closes a block in a sequence of lines,
+  /// \c MatchingOpeningBlockLineIndex stores the index of the corresponding
+  /// opening line. Otherwise, \c MatchingOpeningBlockLineIndex must be
+  /// \c kInvalidIndex.
+  size_t MatchingOpeningBlockLineIndex;
+
+  static const size_t kInvalidIndex = -1;
 };
 
 class UnwrappedLineConsumer {
@@ -234,8 +242,8 @@ struct UnwrappedLineNode {
   SmallVector<UnwrappedLine, 0> Children;
 };
 
-inline UnwrappedLine::UnwrappedLine()
-    : Level(0), InPPDirective(false), MustBeDeclaration(false) {}
+inline UnwrappedLine::UnwrappedLine() : Level(0), InPPDirective(false),
+  MustBeDeclaration(false), MatchingOpeningBlockLineIndex(kInvalidIndex) {}
 
 } // end namespace format
 } // end namespace clang

Modified: cfe/trunk/unittests/Format/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Format/CMakeLists.txt?rev=296341&r1=296340&r2=296341&view=diff
==============================================================================
--- cfe/trunk/unittests/Format/CMakeLists.txt (original)
+++ cfe/trunk/unittests/Format/CMakeLists.txt Mon Feb 27 07:28:36 2017
@@ -6,11 +6,12 @@ add_clang_unittest(FormatTests
   CleanupTest.cpp
   FormatTest.cpp
   FormatTestComments.cpp
-  FormatTestJava.cpp
   FormatTestJS.cpp
+  FormatTestJava.cpp
   FormatTestObjC.cpp
   FormatTestProto.cpp
   FormatTestSelective.cpp
+  NamespaceEndCommentsFixerTest.cpp
   SortImportsTestJS.cpp
   SortIncludesTest.cpp
   )

Added: cfe/trunk/unittests/Format/NamespaceEndCommentsFixerTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Format/NamespaceEndCommentsFixerTest.cpp?rev=296341&view=auto
==============================================================================
--- cfe/trunk/unittests/Format/NamespaceEndCommentsFixerTest.cpp (added)
+++ cfe/trunk/unittests/Format/NamespaceEndCommentsFixerTest.cpp Mon Feb 27 07:28:36 2017
@@ -0,0 +1,350 @@
+//===- NamespaceEndCommentsFixerTest.cpp - Formatting unit tests ----------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Format/Format.h"
+
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "llvm/Support/Debug.h"
+#include "gtest/gtest.h"
+
+#define DEBUG_TYPE "namespace-end-comments-fixer-test"
+
+namespace clang {
+namespace format {
+namespace {
+
+class NamespaceEndCommentsFixerTest : public ::testing::Test {
+protected:
+  std::string
+  fixNamespaceEndComments(llvm::StringRef Code,
+                          std::vector<tooling::Range> Ranges,
+                          const FormatStyle &Style = getLLVMStyle()) {
+    DEBUG(llvm::errs() << "---\n");
+    DEBUG(llvm::errs() << Code << "\n\n");
+    tooling::Replacements Replaces =
+        clang::format::fixNamespaceEndComments(Style, Code, Ranges, "<stdin>");
+    auto Result = applyAllReplacements(Code, Replaces);
+    EXPECT_TRUE(static_cast<bool>(Result));
+    DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
+    return *Result;
+  }
+
+  std::string
+  fixNamespaceEndComments(llvm::StringRef Code,
+                          const FormatStyle &Style = getLLVMStyle()) {
+    return fixNamespaceEndComments(
+        Code,
+        /*Ranges=*/{1, tooling::Range(0, Code.size())}, Style);
+  }
+};
+
+TEST_F(NamespaceEndCommentsFixerTest, AddsEndComment) {
+  EXPECT_EQ("namespace {\n"
+            "  int i;\n"
+            "}// namespace",
+            fixNamespaceEndComments("namespace {\n"
+                                    "  int i;\n"
+                                    "}"));
+  EXPECT_EQ("namespace {\n"
+            "  int i;\n"
+            "}// namespace\n",
+            fixNamespaceEndComments("namespace {\n"
+                                    "  int i;\n"
+                                    "}\n"));
+  EXPECT_EQ("namespace A {\n"
+            "  int i;\n"
+            "}// namespace A",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int i;\n"
+                                    "}"));
+  EXPECT_EQ("inline namespace A {\n"
+            "  int i;\n"
+            "}// namespace A",
+            fixNamespaceEndComments("inline namespace A {\n"
+                                    "  int i;\n"
+                                    "}"));
+  EXPECT_EQ("namespace ::A {\n"
+            "  int i;\n"
+            "}// namespace ::A",
+            fixNamespaceEndComments("namespace ::A {\n"
+                                    "  int i;\n"
+                                    "}"));
+  EXPECT_EQ("namespace ::A::B {\n"
+            "  int i;\n"
+            "}// namespace ::A::B",
+            fixNamespaceEndComments("namespace ::A::B {\n"
+                                    "  int i;\n"
+                                    "}"));
+  EXPECT_EQ("namespace /**/::/**/A/**/::/**/B/**/ {\n"
+            "  int i;\n"
+            "}// namespace ::A::B",
+            fixNamespaceEndComments("namespace /**/::/**/A/**/::/**/B/**/ {\n"
+                                    "  int i;\n"
+                                    "}"));
+  EXPECT_EQ("namespace A {\n"
+            "namespace B {\n"
+            "  int i;\n"
+            "}// namespace B\n"
+            "}// namespace A",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "namespace B {\n"
+                                    "  int i;\n"
+                                    "}\n"
+                                    "}"));
+  EXPECT_EQ("namespace A {\n"
+            "  int a;\n"
+            "}// namespace A\n"
+            "namespace B {\n"
+            "  int b;\n"
+            "}// namespace B",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int a;\n"
+                                    "}\n"
+                                    "namespace B {\n"
+                                    "  int b;\n"
+                                    "}"));
+  EXPECT_EQ("namespace A {\n"
+            "  int a1;\n"
+            "}// namespace A\n"
+            "namespace A {\n"
+            "  int a2;\n"
+            "}// namespace A",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int a1;\n"
+                                    "}\n"
+                                    "namespace A {\n"
+                                    "  int a2;\n"
+                                    "}"));
+  EXPECT_EQ("namespace A {\n"
+            "  int a;\n"
+            "}// namespace A\n"
+            "// comment about b\n"
+            "int b;",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int a;\n"
+                                    "}\n"
+                                    "// comment about b\n"
+                                    "int b;"));
+
+  EXPECT_EQ("namespace A {\n"
+            "namespace B {\n"
+            "namespace C {\n"
+            "namespace D {\n"
+            "}// namespace D\n"
+            "}// namespace C\n"
+            "}// namespace B\n"
+            "}// namespace A",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "namespace B {\n"
+                                    "namespace C {\n"
+                                    "namespace D {\n"
+                                    "}\n"
+                                    "}\n"
+                                    "}\n"
+                                    "}"));
+}
+
+TEST_F(NamespaceEndCommentsFixerTest, AddsNewlineIfNeeded) {
+  EXPECT_EQ("namespace A {\n"
+            "  int i;\n"
+            "}// namespace A\n"
+            " int j;",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int i;\n"
+                                    "} int j;"));
+  EXPECT_EQ("namespace {\n"
+            "  int i;\n"
+            "}// namespace\n"
+            " int j;",
+            fixNamespaceEndComments("namespace {\n"
+                                    "  int i;\n"
+                                    "} int j;"));
+  EXPECT_EQ("namespace A {\n"
+            "  int i;\n"
+            "}// namespace A\n"
+            " namespace B {\n"
+            "  int j;\n"
+            "}// namespace B",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int i;\n"
+                                    "} namespace B {\n"
+                                    "  int j;\n"
+                                    "}"));
+}
+
+TEST_F(NamespaceEndCommentsFixerTest, DoesNotAddEndCommentForShortNamespace) {
+  EXPECT_EQ("namespace {}", fixNamespaceEndComments("namespace {}"));
+  EXPECT_EQ("namespace A {}", fixNamespaceEndComments("namespace A {}"));
+  EXPECT_EQ("namespace A { int i; }",
+            fixNamespaceEndComments("namespace A { int i; }"));
+}
+
+TEST_F(NamespaceEndCommentsFixerTest, DoesNotAddCommentAfterUnaffectedRBrace) {
+  EXPECT_EQ("namespace A {\n"
+            "  int i;\n"
+            "}",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int i;\n"
+                                    "}",
+                                    // The range (16, 3) spans the 'int' above.
+                                    /*Ranges=*/{1, tooling::Range(16, 3)}));
+}
+
+TEST_F(NamespaceEndCommentsFixerTest, DoesNotAddCommentAfterRBraceInPPDirective) {
+  EXPECT_EQ("#define SAD \\\n"
+            "namespace A { \\\n"
+            "  int i; \\\n"
+            "}",
+            fixNamespaceEndComments("#define SAD \\\n"
+                                    "namespace A { \\\n"
+                                    "  int i; \\\n"
+                                    "}"));
+}
+
+TEST_F(NamespaceEndCommentsFixerTest, KeepsValidEndComment) {
+  EXPECT_EQ("namespace {\n"
+            "  int i;\n"
+            "} // end anonymous namespace",
+            fixNamespaceEndComments("namespace {\n"
+                                    "  int i;\n"
+                                    "} // end anonymous namespace"));
+  EXPECT_EQ("namespace A {\n"
+            "  int i;\n"
+            "} /* end of namespace A */",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int i;\n"
+                                    "} /* end of namespace A */"));
+  EXPECT_EQ("namespace A {\n"
+            "  int i;\n"
+            "}   //   namespace A",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int i;\n"
+                                    "}   //   namespace A"));
+  EXPECT_EQ("namespace A::B {\n"
+            "  int i;\n"
+            "} // end namespace A::B",
+            fixNamespaceEndComments("namespace A::B {\n"
+                                    "  int i;\n"
+                                    "} // end namespace A::B"));
+}
+
+TEST_F(NamespaceEndCommentsFixerTest, UpdatesInvalidEndLineComment) {
+  EXPECT_EQ("namespace {\n"
+            "  int i;\n"
+            "} // namespace",
+            fixNamespaceEndComments("namespace {\n"
+                                    "  int i;\n"
+                                    "} // namespace A"));
+  EXPECT_EQ("namespace A {\n"
+            "  int i;\n"
+            "} // namespace A",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int i;\n"
+                                    "} // namespace"));
+  EXPECT_EQ("namespace A {\n"
+            "  int i;\n"
+            "} // namespace A",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int i;\n"
+                                    "} //"));
+  EXPECT_EQ("namespace A {\n"
+            "  int i;\n"
+            "} // namespace A",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int i;\n"
+                                    "} //"));
+  EXPECT_EQ("namespace A {\n"
+            "  int i;\n"
+            "} // namespace A",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int i;\n"
+                                    "} // banamespace A"));
+
+  // Updates invalid line comments even for short namespaces.
+  EXPECT_EQ("namespace A {} // namespace A",
+            fixNamespaceEndComments("namespace A {} // namespace"));
+}
+
+TEST_F(NamespaceEndCommentsFixerTest, UpdatesInvalidEndBlockComment) {
+  EXPECT_EQ("namespace {\n"
+            "  int i;\n"
+            "} // namespace",
+            fixNamespaceEndComments("namespace {\n"
+                                    "  int i;\n"
+                                    "} /* namespace A */"));
+  EXPECT_EQ("namespace A {\n"
+            "  int i;\n"
+            "}  // namespace A",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int i;\n"
+                                    "}  /* end namespace */"));
+  EXPECT_EQ("namespace A {\n"
+            "  int i;\n"
+            "} // namespace A",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int i;\n"
+                                    "} /**/"));
+  EXPECT_EQ("namespace A {\n"
+            "  int i;\n"
+            "} // namespace A",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int i;\n"
+                                    "} /* end unnamed namespace */"));
+  EXPECT_EQ("namespace A {\n"
+            "  int i;\n"
+            "} // namespace A",
+            fixNamespaceEndComments("namespace A {\n"
+                                    "  int i;\n"
+                                    "} /* banamespace A */"));
+  EXPECT_EQ("namespace A {} // namespace A",
+            fixNamespaceEndComments("namespace A {} /**/"));
+}
+
+TEST_F(NamespaceEndCommentsFixerTest,
+       DoesNotAddEndCommentForNamespacesControlledByMacros) {
+  EXPECT_EQ("#ifdef 1\n"
+            "namespace A {\n"
+            "#elseif\n"
+            "namespace B {\n"
+            "#endif\n"
+            "  int i;\n"
+            "}\n"
+            "}\n",
+            fixNamespaceEndComments("#ifdef 1\n"
+                                    "namespace A {\n"
+                                    "#elseif\n"
+                                    "namespace B {\n"
+                                    "#endif\n"
+                                    "  int i;\n"
+                                    "}\n"
+                                    "}\n"));
+}
+
+TEST_F(NamespaceEndCommentsFixerTest,
+       DoesNotAddEndCommentForNamespacesInMacroDeclarations) {
+  EXPECT_EQ("#ifdef 1\n"
+            "namespace A {\n"
+            "#elseif\n"
+            "namespace B {\n"
+            "#endif\n"
+            "  int i;\n"
+            "}\n"
+            "}\n",
+            fixNamespaceEndComments("#ifdef 1\n"
+                                    "namespace A {\n"
+                                    "#elseif\n"
+                                    "namespace B {\n"
+                                    "#endif\n"
+                                    "  int i;\n"
+                                    "}\n"
+                                    "}\n"));
+}
+} // end namespace
+} // end namespace format
+} // end namespace clang




More information about the cfe-commits mailing list