[clang-tools-extra] r281918 - A clang tool for changing surrouding namespaces of class/function definitions.

Eric Liu via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 19 10:40:33 PDT 2016


Author: ioeric
Date: Mon Sep 19 12:40:32 2016
New Revision: 281918

URL: http://llvm.org/viewvc/llvm-project?rev=281918&view=rev
Log:
A clang tool for changing surrouding namespaces of class/function definitions.

Summary:
A tool for changing surrouding namespaces of class/function definitions while keeping
references to types in the changed namespace correctly qualified by prepending
namespace specifiers before them.

Example: test.cc
   namespace na {
   class X {};
   namespace nb {
   class Y { X x; };
   } // namespace nb
   } // namespace na

To move the definition of class Y from namespace "na::nb" to "x::y", run:
   clang-change-namespace --old_namespace "na::nb" \
     --new_namespace "x::y" --file_pattern "test.cc" test.cc --

Output:
   namespace na {
   class X {};
   } // namespace na
   namespace x {
   namespace y {
   class Y { na::X x; };
   } // namespace y
   } // namespace x

Reviewers: alexfh, omtcyfz, hokein

Subscribers: mgorny, klimek, djasper, beanz, alexshap, Eugene.Zelenko, cfe-commits

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

Added:
    clang-tools-extra/trunk/change-namespace/
    clang-tools-extra/trunk/change-namespace/CMakeLists.txt
    clang-tools-extra/trunk/change-namespace/ChangeNamespace.cpp
    clang-tools-extra/trunk/change-namespace/ChangeNamespace.h
    clang-tools-extra/trunk/change-namespace/tool/
    clang-tools-extra/trunk/change-namespace/tool/CMakeLists.txt
    clang-tools-extra/trunk/change-namespace/tool/ClangChangeNamespace.cpp
    clang-tools-extra/trunk/test/change-namespace/
    clang-tools-extra/trunk/test/change-namespace/simple-move.cpp
    clang-tools-extra/trunk/unittests/change-namespace/
    clang-tools-extra/trunk/unittests/change-namespace/CMakeLists.txt
    clang-tools-extra/trunk/unittests/change-namespace/ChangeNamespaceTests.cpp
Modified:
    clang-tools-extra/trunk/CMakeLists.txt
    clang-tools-extra/trunk/test/CMakeLists.txt
    clang-tools-extra/trunk/unittests/CMakeLists.txt

Modified: clang-tools-extra/trunk/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/CMakeLists.txt?rev=281918&r1=281917&r2=281918&view=diff
==============================================================================
--- clang-tools-extra/trunk/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/CMakeLists.txt Mon Sep 19 12:40:32 2016
@@ -7,6 +7,7 @@ add_subdirectory(clang-tidy)
 add_subdirectory(clang-tidy-vs)
 endif()
 
+add_subdirectory(change-namespace)
 add_subdirectory(clang-query)
 add_subdirectory(include-fixer)
 add_subdirectory(pp-trace)

Added: clang-tools-extra/trunk/change-namespace/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/change-namespace/CMakeLists.txt?rev=281918&view=auto
==============================================================================
--- clang-tools-extra/trunk/change-namespace/CMakeLists.txt (added)
+++ clang-tools-extra/trunk/change-namespace/CMakeLists.txt Mon Sep 19 12:40:32 2016
@@ -0,0 +1,19 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+add_clang_library(clangChangeNamespace
+  ChangeNamespace.cpp
+
+  LINK_LIBS
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFormat
+  clangFrontend
+  clangLex
+  clangTooling
+  clangToolingCore
+  )
+
+add_subdirectory(tool)

Added: clang-tools-extra/trunk/change-namespace/ChangeNamespace.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/change-namespace/ChangeNamespace.cpp?rev=281918&view=auto
==============================================================================
--- clang-tools-extra/trunk/change-namespace/ChangeNamespace.cpp (added)
+++ clang-tools-extra/trunk/change-namespace/ChangeNamespace.cpp Mon Sep 19 12:40:32 2016
@@ -0,0 +1,509 @@
+//===-- ChangeNamespace.cpp - Change namespace implementation -------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "ChangeNamespace.h"
+#include "clang/Format/Format.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace change_namespace {
+
+namespace {
+
+inline std::string
+joinNamespaces(const llvm::SmallVectorImpl<StringRef> &Namespaces) {
+  if (Namespaces.empty())
+    return "";
+  std::string Result = Namespaces.front();
+  for (auto I = Namespaces.begin() + 1, E = Namespaces.end(); I != E; ++I)
+    Result += ("::" + *I).str();
+  return Result;
+}
+
+SourceLocation startLocationForType(TypeLoc TLoc) {
+  // For elaborated types (e.g. `struct a::A`) we want the portion after the
+  // `struct` but including the namespace qualifier, `a::`.
+  if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) {
+    NestedNameSpecifierLoc NestedNameSpecifier =
+        TLoc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
+    if (NestedNameSpecifier.getNestedNameSpecifier())
+      return NestedNameSpecifier.getBeginLoc();
+    TLoc = TLoc.getNextTypeLoc();
+  }
+  return TLoc.getLocStart();
+}
+
+SourceLocation EndLocationForType(TypeLoc TLoc) {
+  // Dig past any namespace or keyword qualifications.
+  while (TLoc.getTypeLocClass() == TypeLoc::Elaborated ||
+         TLoc.getTypeLocClass() == TypeLoc::Qualified)
+    TLoc = TLoc.getNextTypeLoc();
+
+  // The location for template specializations (e.g. Foo<int>) includes the
+  // templated types in its location range.  We want to restrict this to just
+  // before the `<` character.
+  if (TLoc.getTypeLocClass() == TypeLoc::TemplateSpecialization)
+    return TLoc.castAs<TemplateSpecializationTypeLoc>()
+        .getLAngleLoc()
+        .getLocWithOffset(-1);
+  return TLoc.getEndLoc();
+}
+
+// Returns the containing namespace of `InnerNs` by skipping `PartialNsName`.
+// If the `InnerNs` does not have `PartialNsName` as suffix, nullptr is
+// returned.
+// For example, if `InnerNs` is "a::b::c" and `PartialNsName` is "b::c", then
+// the NamespaceDecl of namespace "a" will be returned.
+const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs,
+                                       llvm::StringRef PartialNsName) {
+  const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs);
+  const auto *CurrentNs = InnerNs;
+  llvm::SmallVector<llvm::StringRef, 4> PartialNsNameSplitted;
+  PartialNsName.split(PartialNsNameSplitted, "::");
+  while (!PartialNsNameSplitted.empty()) {
+    // Get the inner-most namespace in CurrentContext.
+    while (CurrentContext && !llvm::isa<NamespaceDecl>(CurrentContext))
+      CurrentContext = CurrentContext->getParent();
+    if (!CurrentContext)
+      return nullptr;
+    CurrentNs = llvm::cast<NamespaceDecl>(CurrentContext);
+    if (PartialNsNameSplitted.back() != CurrentNs->getNameAsString())
+      return nullptr;
+    PartialNsNameSplitted.pop_back();
+    CurrentContext = CurrentContext->getParent();
+  }
+  return CurrentNs;
+}
+
+// FIXME: get rid of this helper function if this is supported in clang-refactor
+// library.
+SourceLocation getStartOfNextLine(SourceLocation Loc, const SourceManager &SM,
+                                  const LangOptions &LangOpts) {
+  if (Loc.isMacroID() &&
+      !Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc))
+    return SourceLocation();
+  // Break down the source location.
+  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
+  // Try to load the file buffer.
+  bool InvalidTemp = false;
+  llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
+  if (InvalidTemp)
+    return SourceLocation();
+
+  const char *TokBegin = File.data() + LocInfo.second;
+  // Lex from the start of the given location.
+  Lexer Lex(SM.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(),
+            TokBegin, File.end());
+
+  llvm::SmallVector<char, 16> Line;
+  // FIXME: this is a bit hacky to get ReadToEndOfLine work.
+  Lex.setParsingPreprocessorDirective(true);
+  Lex.ReadToEndOfLine(&Line);
+  // FIXME: should not +1 at EOF.
+  return Loc.getLocWithOffset(Line.size() + 1);
+}
+
+// Returns `R` with new range that refers to code after `Replaces` being
+// applied.
+tooling::Replacement
+getReplacementInChangedCode(const tooling::Replacements &Replaces,
+                            const tooling::Replacement &R) {
+  unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset());
+  unsigned NewEnd =
+      Replaces.getShiftedCodePosition(R.getOffset() + R.getLength());
+  return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,
+                              R.getReplacementText());
+}
+
+// Adds a replacement `R` into `Replaces` or merges it into `Replaces` by
+// applying all existing Replaces first if there is conflict.
+void addOrMergeReplacement(const tooling::Replacement &R,
+                           tooling::Replacements *Replaces) {
+  auto Err = Replaces->add(R);
+  if (Err) {
+    llvm::consumeError(std::move(Err));
+    auto Replace = getReplacementInChangedCode(*Replaces, R);
+    *Replaces = Replaces->merge(tooling::Replacements(Replace));
+  }
+}
+
+tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End,
+                                       llvm::StringRef ReplacementText,
+                                       const SourceManager &SM) {
+  if (!Start.isValid() || !End.isValid()) {
+    llvm::errs() << "start or end location were invalid\n";
+    return tooling::Replacement();
+  }
+  if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) {
+    llvm::errs()
+        << "start or end location were in different macro expansions\n";
+    return tooling::Replacement();
+  }
+  Start = SM.getSpellingLoc(Start);
+  End = SM.getSpellingLoc(End);
+  if (SM.getFileID(Start) != SM.getFileID(End)) {
+    llvm::errs() << "start or end location were in different files\n";
+    return tooling::Replacement();
+  }
+  return tooling::Replacement(
+      SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
+                                         SM.getSpellingLoc(End)),
+      ReplacementText);
+}
+
+tooling::Replacement createInsertion(SourceLocation Loc,
+                                     llvm::StringRef InsertText,
+                                     const SourceManager &SM) {
+  if (Loc.isInvalid()) {
+    llvm::errs() << "insert Location is invalid.\n";
+    return tooling::Replacement();
+  }
+  Loc = SM.getSpellingLoc(Loc);
+  return tooling::Replacement(SM, Loc, 0, InsertText);
+}
+
+// Returns the shortest qualified name for declaration `DeclName` in the
+// namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName`
+// is "a::c::d", then "b::X" will be returned.
+std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName,
+                                                llvm::StringRef NsName) {
+  llvm::SmallVector<llvm::StringRef, 4> DeclNameSplitted;
+  DeclName.split(DeclNameSplitted, "::");
+  if (DeclNameSplitted.size() == 1)
+    return DeclName;
+  const auto UnqualifiedName = DeclNameSplitted.back();
+  while (true) {
+    const auto Pos = NsName.find_last_of(':');
+    if (Pos == llvm::StringRef::npos)
+      return DeclName;
+    const auto Prefix = NsName.substr(0, Pos - 1);
+    if (DeclName.startswith(Prefix))
+      return (Prefix + "::" + UnqualifiedName).str();
+    NsName = Prefix;
+  }
+  return DeclName;
+}
+
+std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) {
+  if (Code.back() != '\n')
+    Code += "\n";
+  llvm::SmallVector<StringRef, 4> NsSplitted;
+  NestedNs.split(NsSplitted, "::");
+  while (!NsSplitted.empty()) {
+    // FIXME: consider code style for comments.
+    Code = ("namespace " + NsSplitted.back() + " {\n" + Code +
+            "} // namespace " + NsSplitted.back() + "\n")
+               .str();
+    NsSplitted.pop_back();
+  }
+  return Code;
+}
+
+} // anonymous namespace
+
+ChangeNamespaceTool::ChangeNamespaceTool(
+    llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
+    std::map<std::string, tooling::Replacements> *FileToReplacements,
+    llvm::StringRef FallbackStyle)
+    : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements),
+      OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')),
+      FilePattern(FilePattern) {
+  FileToReplacements->clear();
+  llvm::SmallVector<llvm::StringRef, 4> OldNsSplitted;
+  llvm::SmallVector<llvm::StringRef, 4> NewNsSplitted;
+  llvm::StringRef(OldNamespace).split(OldNsSplitted, "::");
+  llvm::StringRef(NewNamespace).split(NewNsSplitted, "::");
+  // Calculates `DiffOldNamespace` and `DiffNewNamespace`.
+  while (!OldNsSplitted.empty() && !NewNsSplitted.empty() &&
+         OldNsSplitted.front() == NewNsSplitted.front()) {
+    OldNsSplitted.erase(OldNsSplitted.begin());
+    NewNsSplitted.erase(NewNsSplitted.begin());
+  }
+  DiffOldNamespace = joinNamespaces(OldNsSplitted);
+  DiffNewNamespace = joinNamespaces(NewNsSplitted);
+}
+
+// FIXME: handle the following symbols:
+//   - Types in `UsingShadowDecl` (e.g. `using a::b::c;`) which are not matched
+//   by `typeLoc`.
+//   - Types in nested name specifier, e.g. "na::X" in "na::X::Nested".
+//   - Function references.
+//   - Variable references.
+void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
+  // Match old namespace blocks.
+  std::string FullOldNs = "::" + OldNamespace;
+  Finder->addMatcher(
+      namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern))
+          .bind("old_ns"),
+      this);
+
+  auto IsInMovedNs =
+      allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")),
+            isExpansionInFileMatching(FilePattern));
+
+  // Match forward-declarations in the old namespace.
+  Finder->addMatcher(
+      cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())), IsInMovedNs)
+          .bind("fwd_decl"),
+      this);
+
+  // Match references to types that are not defined in the old namespace.
+  // Forward-declarations in the old namespace are also matched since they will
+  // be moved back to the old namespace.
+  auto DeclMatcher = namedDecl(
+      hasAncestor(namespaceDecl()),
+      unless(anyOf(
+          hasAncestor(namespaceDecl(isAnonymous())),
+          hasAncestor(cxxRecordDecl()),
+          allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition())))))));
+  // Match TypeLocs on the declaration. Carefully match only the outermost
+  // TypeLoc that's directly linked to the old class and don't handle nested
+  // name specifier locs.
+  // FIXME: match and handle nested name specifier locs.
+  Finder->addMatcher(
+      typeLoc(IsInMovedNs,
+              loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))),
+              unless(anyOf(hasParent(typeLoc(
+                               loc(qualType(hasDeclaration(DeclMatcher))))),
+                           hasParent(nestedNameSpecifierLoc()))),
+              hasAncestor(decl().bind("dc")))
+          .bind("type"),
+      this);
+}
+
+void ChangeNamespaceTool::run(
+    const ast_matchers::MatchFinder::MatchResult &Result) {
+  if (const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) {
+    moveOldNamespace(Result, NsDecl);
+  } else if (const auto *FwdDecl =
+                 Result.Nodes.getNodeAs<CXXRecordDecl>("fwd_decl")) {
+    moveClassForwardDeclaration(Result, FwdDecl);
+  } else {
+    const auto *TLoc = Result.Nodes.getNodeAs<TypeLoc>("type");
+    assert(TLoc != nullptr && "Expecting callback for TypeLoc");
+    fixTypeLoc(Result, startLocationForType(*TLoc), EndLocationForType(*TLoc),
+               *TLoc);
+  }
+}
+
+// Stores information about a moved namespace in `MoveNamespaces` and leaves
+// the actual movement to `onEndOfTranslationUnit()`.
+void ChangeNamespaceTool::moveOldNamespace(
+    const ast_matchers::MatchFinder::MatchResult &Result,
+    const NamespaceDecl *NsDecl) {
+  // If the namespace is empty, do nothing.
+  if (Decl::castToDeclContext(NsDecl)->decls_empty())
+    return;
+
+  // Get the range of the code in the old namespace.
+  SourceLocation Start = NsDecl->decls_begin()->getLocStart();
+  SourceLocation End = NsDecl->getRBraceLoc().getLocWithOffset(-1);
+  // Create a replacement that deletes the code in the old namespace merely for
+  // retrieving offset and length from it.
+  const auto R = createReplacement(Start, End, "", *Result.SourceManager);
+  MoveNamespace MoveNs;
+  MoveNs.Offset = R.getOffset();
+  MoveNs.Length = R.getLength();
+
+  // Insert the new namespace after `DiffOldNamespace`. For example, if
+  // `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then
+  // "x::y" will be inserted inside the existing namespace "a" and after "a::b".
+  // `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b"
+  // in the above example.
+  // FIXME: consider the case where DiffOldNamespace is empty.
+  const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace);
+  SourceLocation LocAfterNs =
+      getStartOfNextLine(OuterNs->getRBraceLoc(), *Result.SourceManager,
+                         Result.Context->getLangOpts());
+  assert(LocAfterNs.isValid() &&
+         "Failed to get location after DiffOldNamespace");
+  MoveNs.InsertionOffset = Result.SourceManager->getFileOffset(
+      Result.SourceManager->getSpellingLoc(LocAfterNs));
+
+  MoveNs.FileID = Result.SourceManager->getFileID(Start);
+  MoveNs.SourceManager = Result.SourceManager;
+  MoveNamespaces[R.getFilePath()].push_back(MoveNs);
+}
+
+// Removes a class forward declaration from the code in the moved namespace and
+// creates an `InsertForwardDeclaration` to insert the forward declaration back
+// into the old namespace after moving code from the old namespace to the new
+// namespace.
+// For example, changing "a" to "x":
+// Old code:
+//   namespace a {
+//   class FWD;
+//   class A { FWD *fwd; }
+//   }  // a
+// New code:
+//   namespace a {
+//   class FWD;
+//   }  // a
+//   namespace x {
+//   class A { a::FWD *fwd; }
+//   }  // x
+void ChangeNamespaceTool::moveClassForwardDeclaration(
+    const ast_matchers::MatchFinder::MatchResult &Result,
+    const CXXRecordDecl *FwdDecl) {
+  SourceLocation Start = FwdDecl->getLocStart();
+  SourceLocation End = FwdDecl->getLocEnd();
+  SourceLocation AfterSemi = Lexer::findLocationAfterToken(
+      End, tok::semi, *Result.SourceManager, Result.Context->getLangOpts(),
+      /*SkipTrailingWhitespaceAndNewLine=*/true);
+  if (AfterSemi.isValid())
+    End = AfterSemi.getLocWithOffset(-1);
+  // Delete the forward declaration from the code to be moved.
+  const auto Deletion =
+      createReplacement(Start, End, "", *Result.SourceManager);
+  addOrMergeReplacement(Deletion, &FileToReplacements[Deletion.getFilePath()]);
+  llvm::StringRef Code = Lexer::getSourceText(
+      CharSourceRange::getTokenRange(
+          Result.SourceManager->getSpellingLoc(Start),
+          Result.SourceManager->getSpellingLoc(End)),
+      *Result.SourceManager, Result.Context->getLangOpts());
+  // Insert the forward declaration back into the old namespace after moving the
+  // code from old namespace to new namespace.
+  // Insertion information is stored in `InsertFwdDecls` and actual
+  // insertion will be performed in `onEndOfTranslationUnit`.
+  // Get the (old) namespace that contains the forward declaration.
+  const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl");
+  // The namespace contains the forward declaration, so it must not be empty.
+  assert(!NsDecl->decls_empty());
+  const auto Insertion = createInsertion(NsDecl->decls_begin()->getLocStart(),
+                                         Code, *Result.SourceManager);
+  InsertForwardDeclaration InsertFwd;
+  InsertFwd.InsertionOffset = Insertion.getOffset();
+  InsertFwd.ForwardDeclText = Insertion.getReplacementText().str();
+  InsertFwdDecls[Insertion.getFilePath()].push_back(InsertFwd);
+}
+
+// Replaces a qualified symbol that refers to a declaration `DeclName` with the
+// shortest qualified name possible when the reference is in `NewNamespace`.
+void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
+    const ast_matchers::MatchFinder::MatchResult &Result, const Decl *DeclCtx,
+    SourceLocation Start, SourceLocation End, llvm::StringRef DeclName) {
+  const auto *NsDeclContext =
+      DeclCtx->getDeclContext()->getEnclosingNamespaceContext();
+  const auto *NsDecl = llvm::dyn_cast<NamespaceDecl>(NsDeclContext);
+  // Calculate the name of the `NsDecl` after it is moved to new namespace.
+  std::string OldNs = NsDecl->getQualifiedNameAsString();
+  llvm::StringRef Postfix = OldNs;
+  bool Consumed = Postfix.consume_front(OldNamespace);
+  assert(Consumed && "Expect OldNS to start with OldNamespace.");
+  (void)Consumed;
+  const std::string NewNs = (NewNamespace + Postfix).str();
+
+  llvm::StringRef NestedName = Lexer::getSourceText(
+      CharSourceRange::getTokenRange(
+          Result.SourceManager->getSpellingLoc(Start),
+          Result.SourceManager->getSpellingLoc(End)),
+      *Result.SourceManager, Result.Context->getLangOpts());
+  // If the symbol is already fully qualified, no change needs to be make.
+  if (NestedName.startswith("::"))
+    return;
+  std::string ReplaceName =
+      getShortestQualifiedNameInNamespace(DeclName, NewNs);
+  // If the new nested name in the new namespace is the same as it was in the
+  // old namespace, we don't create replacement.
+  if (NestedName == ReplaceName)
+    return;
+  auto R = createReplacement(Start, End, ReplaceName, *Result.SourceManager);
+  addOrMergeReplacement(R, &FileToReplacements[R.getFilePath()]);
+}
+
+// Replace the [Start, End] of `Type` with the shortest qualified name when the
+// `Type` is in `NewNamespace`.
+void ChangeNamespaceTool::fixTypeLoc(
+    const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start,
+    SourceLocation End, TypeLoc Type) {
+  // FIXME: do not rename template parameter.
+  if (Start.isInvalid() || End.isInvalid())
+    return;
+  // The declaration which this TypeLoc refers to.
+  const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl");
+  // `hasDeclaration` gives underlying declaration, but if the type is
+  // a typedef type, we need to use the typedef type instead.
+  if (auto *Typedef = Type.getType()->getAs<TypedefType>())
+    FromDecl = Typedef->getDecl();
+
+  const Decl *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc");
+  assert(DeclCtx && "Empty decl context.");
+  replaceQualifiedSymbolInDeclContext(Result, DeclCtx, Start, End,
+                                      FromDecl->getQualifiedNameAsString());
+}
+
+void ChangeNamespaceTool::onEndOfTranslationUnit() {
+  // Move namespace blocks and insert forward declaration to old namespace.
+  for (const auto &FileAndNsMoves : MoveNamespaces) {
+    auto &NsMoves = FileAndNsMoves.second;
+    if (NsMoves.empty())
+      continue;
+    const std::string &FilePath = FileAndNsMoves.first;
+    auto &Replaces = FileToReplacements[FilePath];
+    auto &SM = *NsMoves.begin()->SourceManager;
+    llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FileID);
+    auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);
+    if (!ChangedCode) {
+      llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
+      continue;
+    }
+    // Replacements on the changed code for moving namespaces and inserting
+    // forward declarations to old namespaces.
+    tooling::Replacements NewReplacements;
+    // Cut the changed code from the old namespace and paste the code in the new
+    // namespace.
+    for (const auto &NsMove : NsMoves) {
+      // Calculate the range of the old namespace block in the changed
+      // code.
+      const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset);
+      const unsigned NewLength =
+          Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) -
+          NewOffset;
+      tooling::Replacement Deletion(FilePath, NewOffset, NewLength, "");
+      std::string MovedCode = ChangedCode->substr(NewOffset, NewLength);
+      std::string MovedCodeWrappedInNewNs =
+          wrapCodeInNamespace(DiffNewNamespace, MovedCode);
+      // Calculate the new offset at which the code will be inserted in the
+      // changed code.
+      unsigned NewInsertionOffset =
+          Replaces.getShiftedCodePosition(NsMove.InsertionOffset);
+      tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
+                                     MovedCodeWrappedInNewNs);
+      addOrMergeReplacement(Deletion, &NewReplacements);
+      addOrMergeReplacement(Insertion, &NewReplacements);
+    }
+    // After moving namespaces, insert forward declarations back to old
+    // namespaces.
+    const auto &FwdDeclInsertions = InsertFwdDecls[FilePath];
+    for (const auto &FwdDeclInsertion : FwdDeclInsertions) {
+      unsigned NewInsertionOffset =
+          Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset);
+      tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
+                                     FwdDeclInsertion.ForwardDeclText);
+      addOrMergeReplacement(Insertion, &NewReplacements);
+    }
+    // Add replacements referring to the changed code to existing replacements,
+    // which refers to the original code.
+    Replaces = Replaces.merge(NewReplacements);
+    format::FormatStyle Style =
+        format::getStyle("file", FilePath, FallbackStyle);
+    // Clean up old namespaces if there is nothing in it after moving.
+    auto CleanReplacements =
+        format::cleanupAroundReplacements(Code, Replaces, Style);
+    if (!CleanReplacements) {
+      llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
+      continue;
+    }
+    FileToReplacements[FilePath] = *CleanReplacements;
+  }
+}
+
+} // namespace change_namespace
+} // namespace clang

Added: clang-tools-extra/trunk/change-namespace/ChangeNamespace.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/change-namespace/ChangeNamespace.h?rev=281918&view=auto
==============================================================================
--- clang-tools-extra/trunk/change-namespace/ChangeNamespace.h (added)
+++ clang-tools-extra/trunk/change-namespace/ChangeNamespace.h Mon Sep 19 12:40:32 2016
@@ -0,0 +1,144 @@
+//===-- ChangeNamespace.h -- Change namespace  ------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Format/Format.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include <string>
+
+namespace clang {
+namespace change_namespace {
+
+// This tool can be used to change the surrounding namespaces of class/function
+// definitions. Classes/functions in the moved namespace will have new
+// namespaces while references to symbols (e.g. types, functions) which are not
+// defined in the changed namespace will be correctly qualified by prepending
+// namespace specifiers before them.
+// For classes, only classes that are declared/defined in the given namespace in
+// speficifed files will be moved: forward declarations will remain in the old
+// namespace.
+// For example, changing "a" to "x":
+// Old code:
+//   namespace a {
+//   class FWD;
+//   class A { FWD *fwd; }
+//   }  // a
+// New code:
+//   namespace a {
+//   class FWD;
+//   }  // a
+//   namespace x {
+//   class A { a::FWD *fwd; }
+//   }  // x
+// FIXME: support moving typedef, enums across namespaces.
+class ChangeNamespaceTool : public ast_matchers::MatchFinder::MatchCallback {
+public:
+  // Moves code in the old namespace `OldNs` to the new namespace `NewNs` in
+  // files matching `FilePattern`.
+  ChangeNamespaceTool(
+      llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
+      std::map<std::string, tooling::Replacements> *FileToReplacements,
+      llvm::StringRef FallbackStyle = "LLVM");
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder);
+
+  void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+  // Moves the changed code in old namespaces but leaves class forward
+  // declarations behind.
+  void onEndOfTranslationUnit() override;
+
+private:
+  void moveOldNamespace(const ast_matchers::MatchFinder::MatchResult &Result,
+                        const NamespaceDecl *NsDecl);
+
+  void moveClassForwardDeclaration(
+      const ast_matchers::MatchFinder::MatchResult &Result,
+      const CXXRecordDecl *FwdDecl);
+
+  void replaceQualifiedSymbolInDeclContext(
+      const ast_matchers::MatchFinder::MatchResult &Result,
+      const Decl *DeclContext, SourceLocation Start, SourceLocation End,
+      llvm::StringRef DeclName);
+
+  void fixTypeLoc(const ast_matchers::MatchFinder::MatchResult &Result,
+                  SourceLocation Start, SourceLocation End, TypeLoc Type);
+
+  // Information about moving an old namespace.
+  struct MoveNamespace {
+    // The start offset of the namespace block being moved in the original
+    // code.
+    unsigned Offset;
+    // The length of the namespace block in the original code.
+    unsigned Length;
+    // The offset at which the new namespace block will be inserted in the
+    // original code.
+    unsigned InsertionOffset;
+    // The file in which the namespace is declared.
+    FileID FileID;
+    SourceManager *SourceManager;
+  };
+
+  // Information about inserting a class forward declaration.
+  struct InsertForwardDeclaration {
+    // The offset at while the forward declaration will be inserted in the
+    // original code.
+    unsigned InsertionOffset;
+    // The code to be inserted.
+    std::string ForwardDeclText;
+  };
+
+  std::string FallbackStyle;
+  // In match callbacks, this contains replacements for replacing `typeLoc`s in
+  // and deleting forward declarations in the moved namespace blocks.
+  // In `onEndOfTranslationUnit` callback, the previous added replacements are
+  // applied (on the moved namespace blocks), and then changed code in old
+  // namespaces re moved to new namespaces, and previously deleted forward
+  // declarations are inserted back to old namespaces, from which they are
+  // deleted.
+  std::map<std::string, tooling::Replacements> &FileToReplacements;
+  // A fully qualified name of the old namespace without "::" prefix, e.g.
+  // "a::b::c".
+  std::string OldNamespace;
+  // A fully qualified name of the new namespace without "::" prefix, e.g.
+  // "x::y::z".
+  std::string NewNamespace;
+  // The longest suffix in the old namespace that does not overlap the new
+  // namespace.
+  // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is
+  // "a::x::y", then `DiffOldNamespace` will be "b::c".
+  std::string DiffOldNamespace;
+  // The longest suffix in the new namespace that does not overlap the old
+  // namespace.
+  // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is
+  // "a::x::y", then `DiffNewNamespace` will be "x::y".
+  std::string DiffNewNamespace;
+  // A regex pattern that matches files to be processed.
+  std::string FilePattern;
+  // Information about moved namespaces grouped by file.
+  // Since we are modifying code in old namespaces (e.g. add namespace
+  // spedifiers) as well as moving them, we store information about namespaces
+  // to be moved and only move them after all modifications are finished (i.e.
+  // in `onEndOfTranslationUnit`).
+  std::map<std::string, std::vector<MoveNamespace>> MoveNamespaces;
+  // Information about forward declaration insertions grouped by files.
+  // A class forward declaration is not moved, so it will be deleted from the
+  // moved code block and inserted back into the old namespace. The insertion
+  // will be done after removing the code from the old namespace and before
+  // inserting it to the new namespace.
+  std::map<std::string, std::vector<InsertForwardDeclaration>> InsertFwdDecls;
+};
+
+} // namespace change_namespace
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H

Added: clang-tools-extra/trunk/change-namespace/tool/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/change-namespace/tool/CMakeLists.txt?rev=281918&view=auto
==============================================================================
--- clang-tools-extra/trunk/change-namespace/tool/CMakeLists.txt (added)
+++ clang-tools-extra/trunk/change-namespace/tool/CMakeLists.txt Mon Sep 19 12:40:32 2016
@@ -0,0 +1,17 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+add_clang_executable(clang-change-namespace
+  ClangChangeNamespace.cpp
+  )
+target_link_libraries(clang-change-namespace
+  clangBasic
+  clangChangeNamespace
+  clangFormat
+  clangFrontend
+  clangRewrite
+  clangTooling
+  clangToolingCore
+  )
+
+install(TARGETS clang-change-namespace
+  RUNTIME DESTINATION bin)

Added: clang-tools-extra/trunk/change-namespace/tool/ClangChangeNamespace.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/change-namespace/tool/ClangChangeNamespace.cpp?rev=281918&view=auto
==============================================================================
--- clang-tools-extra/trunk/change-namespace/tool/ClangChangeNamespace.cpp (added)
+++ clang-tools-extra/trunk/change-namespace/tool/ClangChangeNamespace.cpp Mon Sep 19 12:40:32 2016
@@ -0,0 +1,111 @@
+//===-- ClangIncludeFixer.cpp - Standalone change namespace ---------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This tool can be used to change the surrounding namespaces of class/function
+// definitions.
+//
+// Example: test.cc
+//    namespace na {
+//    class X {};
+//    namespace nb {
+//    class Y { X x; };
+//    } // namespace nb
+//    } // namespace na
+// To move the definition of class Y from namespace "na::nb" to "x::y", run:
+//    clang-change-namespace --old_namespace "na::nb" \
+//      --new_namespace "x::y" --file_pattern "test.cc" test.cc --
+// Output:
+//    namespace na {
+//    class X {};
+//    } // namespace na
+//    namespace x {
+//    namespace y {
+//    class Y { na::X x; };
+//    } // namespace y
+//    } // namespace x
+
+#include "ChangeNamespace.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+
+using namespace clang;
+using namespace llvm;
+
+namespace {
+
+cl::OptionCategory ChangeNamespaceCategory("Change namespace.");
+
+cl::opt<std::string> OldNamespace("old_namespace", cl::Required,
+                                  cl::desc("Old namespace."),
+                                  cl::cat(ChangeNamespaceCategory));
+
+cl::opt<std::string> NewNamespace("new_namespace", cl::Required,
+                                  cl::desc("New namespace."),
+                                  cl::cat(ChangeNamespaceCategory));
+
+cl::opt<std::string> FilePattern(
+    "file_pattern", cl::Required,
+    cl::desc("Only rename namespaces in files that match the given pattern."),
+    cl::cat(ChangeNamespaceCategory));
+
+cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s, if specified."),
+                      cl::cat(ChangeNamespaceCategory));
+
+cl::opt<std::string> Style("style",
+                           cl::desc("The style name used for reformatting."),
+                           cl::init("LLVM"), cl::cat(ChangeNamespaceCategory));
+
+} // anonymous namespace
+
+int main(int argc, const char **argv) {
+  tooling::CommonOptionsParser OptionsParser(argc, argv,
+                                             ChangeNamespaceCategory);
+  const auto &Files = OptionsParser.getSourcePathList();
+  tooling::RefactoringTool Tool(OptionsParser.getCompilations(), Files);
+  change_namespace::ChangeNamespaceTool NamespaceTool(
+      OldNamespace, NewNamespace, FilePattern, &Tool.getReplacements());
+  ast_matchers::MatchFinder Finder;
+  NamespaceTool.registerMatchers(&Finder);
+  std::unique_ptr<tooling::FrontendActionFactory> Factory =
+      tooling::newFrontendActionFactory(&Finder);
+
+  if (int Result = Tool.run(Factory.get()))
+    return Result;
+  LangOptions DefaultLangOptions;
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+  clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
+  DiagnosticsEngine Diagnostics(
+      IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
+      &DiagnosticPrinter, false);
+  auto &FileMgr = Tool.getFiles();
+  SourceManager Sources(Diagnostics, FileMgr);
+  Rewriter Rewrite(Sources, DefaultLangOptions);
+
+  if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) {
+    llvm::errs() << "Failed applying all replacements.\n";
+    return 1;
+  }
+  if (Inplace)
+    return Rewrite.overwriteChangedFiles();
+
+  for (const auto &File : Files) {
+    const auto *Entry = FileMgr.getFile(File);
+
+    auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
+    // FIXME: print results in parsable format, e.g. JSON.
+    outs() << "============== " << File << " ==============\n";
+    Rewrite.getEditBuffer(ID).write(llvm::outs());
+    outs() << "\n============================================\n";
+  }
+  return 0;
+}

Modified: clang-tools-extra/trunk/test/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/CMakeLists.txt?rev=281918&r1=281917&r2=281918&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/test/CMakeLists.txt Mon Sep 19 12:40:32 2016
@@ -42,6 +42,7 @@ set(CLANG_TOOLS_TEST_DEPS
 
   # Individual tools we test.
   clang-apply-replacements
+  clang-change-namespace
   clang-include-fixer
   clang-query
   clang-rename

Added: clang-tools-extra/trunk/test/change-namespace/simple-move.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/change-namespace/simple-move.cpp?rev=281918&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/change-namespace/simple-move.cpp (added)
+++ clang-tools-extra/trunk/test/change-namespace/simple-move.cpp Mon Sep 19 12:40:32 2016
@@ -0,0 +1,10 @@
+// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" %s -- | sed 's,// CHECK.*,,' | FileCheck %s
+// CHECK: namespace x {
+// CHECK-NEXT: namespace y {
+namespace na {
+namespace nb {
+class A {};
+// CHECK: } // namespace y
+// CHECK-NEXT: } // namespace x
+}
+}

Modified: clang-tools-extra/trunk/unittests/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/CMakeLists.txt?rev=281918&r1=281917&r2=281918&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/unittests/CMakeLists.txt Mon Sep 19 12:40:32 2016
@@ -5,6 +5,7 @@ function(add_extra_unittest test_dirname
   add_unittest(ExtraToolsUnitTests ${test_dirname} ${ARGN})
 endfunction()
 
+add_subdirectory(change-namespace)
 add_subdirectory(clang-apply-replacements)
 add_subdirectory(clang-query)
 add_subdirectory(clang-tidy)

Added: clang-tools-extra/trunk/unittests/change-namespace/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/change-namespace/CMakeLists.txt?rev=281918&view=auto
==============================================================================
--- clang-tools-extra/trunk/unittests/change-namespace/CMakeLists.txt (added)
+++ clang-tools-extra/trunk/unittests/change-namespace/CMakeLists.txt Mon Sep 19 12:40:32 2016
@@ -0,0 +1,28 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+get_filename_component(CHANGE_NAMESPACE_SOURCE_DIR
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../change-namespace REALPATH)
+include_directories(
+  ${CHANGE_NAMESPACE_SOURCE_DIR}
+  )
+
+# We'd like clang/unittests/Tooling/RewriterTestContext.h in the test.
+include_directories(${CLANG_SOURCE_DIR})
+
+add_extra_unittest(ChangeNamespaceTests
+  ChangeNamespaceTests.cpp
+  )
+
+target_link_libraries(ChangeNamespaceTests
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangChangeNamespace
+  clangFormat
+  clangFrontend
+  clangRewrite
+  clangTooling
+  clangToolingCore
+  )

Added: clang-tools-extra/trunk/unittests/change-namespace/ChangeNamespaceTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/change-namespace/ChangeNamespaceTests.cpp?rev=281918&view=auto
==============================================================================
--- clang-tools-extra/trunk/unittests/change-namespace/ChangeNamespaceTests.cpp (added)
+++ clang-tools-extra/trunk/unittests/change-namespace/ChangeNamespaceTests.cpp Mon Sep 19 12:40:32 2016
@@ -0,0 +1,234 @@
+//===-- ChangeNamespaceTests.cpp - Change namespace unit tests ---*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ChangeNamespace.h"
+#include "unittests/Tooling/RewriterTestContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/FileSystemOptions.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/PCHContainerOperations.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "gtest/gtest.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace change_namespace {
+namespace {
+
+class ChangeNamespaceTest : public ::testing::Test {
+public:
+  std::string runChangeNamespaceOnCode(llvm::StringRef Code) {
+    clang::RewriterTestContext Context;
+    clang::FileID ID = Context.createInMemoryFile(FileName, Code);
+
+    std::map<std::string, tooling::Replacements> FileToReplacements;
+    change_namespace::ChangeNamespaceTool NamespaceTool(
+        OldNamespace, NewNamespace, FilePattern, &FileToReplacements);
+    ast_matchers::MatchFinder Finder;
+    NamespaceTool.registerMatchers(&Finder);
+    std::unique_ptr<tooling::FrontendActionFactory> Factory =
+        tooling::newFrontendActionFactory(&Finder);
+    tooling::runToolOnCodeWithArgs(Factory->create(), Code, {"-std=c++11"},
+                                   FileName);
+    formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite);
+    return format(Context.getRewrittenText(ID));
+  }
+
+  std::string format(llvm::StringRef Code) {
+    tooling::Replacements Replaces = format::reformat(
+        format::getLLVMStyle(), Code, {tooling::Range(0, Code.size())});
+    auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);
+    EXPECT_TRUE(static_cast<bool>(ChangedCode));
+    if (!ChangedCode) {
+      llvm::errs() << llvm::toString(ChangedCode.takeError());
+      return "";
+    }
+    return *ChangedCode;
+  }
+
+protected:
+  std::string FileName = "input.cc";
+  std::string OldNamespace = "na::nb";
+  std::string NewNamespace = "x::y";
+  std::string FilePattern = "input.cc";
+};
+
+TEST_F(ChangeNamespaceTest, NoMatchingNamespace) {
+  std::string Code = "namespace na {\n"
+                     "namespace nx {\n"
+                     "class A {};\n"
+                     "} // namespace nx\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "namespace nx {\n"
+                         "class A {};\n"
+                         "} // namespace nx\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SimpleMoveWithoutTypeRefs) {
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "class A {};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "\n\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "class A {};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SimpleMoveIntoAnotherNestedNamespace) {
+  NewNamespace = "na::nc";
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "class A {};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "\n"
+                         "namespace nc {\n"
+                         "class A {};\n"
+                         "} // namespace nc\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SimpleMoveNestedNamespace) {
+  NewNamespace = "na::x::y";
+  std::string Code = "namespace na {\n"
+                     "class A {};\n"
+                     "namespace nb {\n"
+                     "class B {};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "class A {};\n"
+                         "\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "class B {};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, SimpleMoveWithTypeRefs) {
+  std::string Code = "namespace na {\n"
+                     "class C_A {};\n"
+                     "namespace nc {\n"
+                     "class C_C {};"
+                     "} // namespace nc\n"
+                     "namespace nb {\n"
+                     "class C_X {\n"
+                     "public:\n"
+                     "  C_A a;\n"
+                     "  nc::C_C c;\n"
+                     "};\n"
+                     "class C_Y {\n"
+                     "  C_X x;\n"
+                     "};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "class C_A {};\n"
+                         "namespace nc {\n"
+                         "class C_C {};"
+                         "} // namespace nc\n"
+                         "\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "class C_X {\n"
+                         "public:\n"
+                         "  na::C_A a;\n"
+                         "  na::nc::C_C c;\n"
+                         "};\n"
+                         "class C_Y {\n"
+                         "  C_X x;\n"
+                         "};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, LeaveForwardDeclarationBehind) {
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "class FWD;\n"
+                     "class A {\n"
+                     "  FWD *fwd;\n"
+                     "};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "namespace nb {\n"
+                         "class FWD;\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "\n"
+                         "class A {\n"
+                         "  na::nb::FWD *fwd;\n"
+                         "};\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, MoveFunctions) {
+  std::string Code = "namespace na {\n"
+                     "class C_A {};\n"
+                     "namespace nc {\n"
+                     "class C_C {};"
+                     "} // namespace nc\n"
+                     "namespace nb {\n"
+                     "void fwd();\n"
+                     "void f(C_A ca, nc::C_C cc) {\n"
+                     "  C_A ca_1 = ca;\n"
+                     "}\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+
+  std::string Expected = "namespace na {\n"
+                         "class C_A {};\n"
+                         "namespace nc {\n"
+                         "class C_C {};"
+                         "} // namespace nc\n"
+                         "\n"
+                         "} // namespace na\n"
+                         "namespace x {\n"
+                         "namespace y {\n"
+                         "void fwd();\n"
+                         "void f(na::C_A ca, na::nc::C_C cc) {\n"
+                         "  na::C_A ca_1 = ca;\n"
+                         "}\n"
+                         "} // namespace y\n"
+                         "} // namespace x\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+} // anonymous namespace
+} // namespace change_namespace
+} // namespace clang




More information about the cfe-commits mailing list