| //===-- ChangeNamespace.cpp - Change namespace implementation -------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | #include "ChangeNamespace.h" | 
 | #include "clang/AST/ASTContext.h" | 
 | #include "clang/Format/Format.h" | 
 | #include "clang/Lex/Lexer.h" | 
 | #include "llvm/Support/Casting.h" | 
 | #include "llvm/Support/ErrorHandling.h" | 
 |  | 
 | using namespace clang::ast_matchers; | 
 |  | 
 | namespace clang { | 
 | namespace change_namespace { | 
 |  | 
 | namespace { | 
 |  | 
 | inline std::string joinNamespaces(ArrayRef<StringRef> Namespaces) { | 
 |   return llvm::join(Namespaces, "::"); | 
 | } | 
 |  | 
 | // Given "a::b::c", returns {"a", "b", "c"}. | 
 | llvm::SmallVector<llvm::StringRef, 4> splitSymbolName(llvm::StringRef Name) { | 
 |   llvm::SmallVector<llvm::StringRef, 4> Splitted; | 
 |   Name.split(Splitted, "::", /*MaxSplit=*/-1, | 
 |              /*KeepEmpty=*/false); | 
 |   return Splitted; | 
 | } | 
 |  | 
 | 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.getBeginLoc(); | 
 | } | 
 |  | 
 | 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, or `PartialNsName` | 
 | // is empty, 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) { | 
 |   if (!InnerNs || PartialNsName.empty()) | 
 |     return nullptr; | 
 |   const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs); | 
 |   const auto *CurrentNs = InnerNs; | 
 |   auto PartialNsNameSplitted = splitSymbolName(PartialNsName); | 
 |   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; | 
 | } | 
 |  | 
 | static std::unique_ptr<Lexer> | 
 | getLexerStartingFromLoc(SourceLocation Loc, const SourceManager &SM, | 
 |                         const LangOptions &LangOpts) { | 
 |   if (Loc.isMacroID() && | 
 |       !Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc)) | 
 |     return nullptr; | 
 |   // 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 nullptr; | 
 |  | 
 |   const char *TokBegin = File.data() + LocInfo.second; | 
 |   // Lex from the start of the given location. | 
 |   return std::make_unique<Lexer>(SM.getLocForStartOfFile(LocInfo.first), | 
 |                                   LangOpts, File.begin(), TokBegin, File.end()); | 
 | } | 
 |  | 
 | // FIXME: get rid of this helper function if this is supported in clang-refactor | 
 | // library. | 
 | static SourceLocation getStartOfNextLine(SourceLocation Loc, | 
 |                                          const SourceManager &SM, | 
 |                                          const LangOptions &LangOpts) { | 
 |   std::unique_ptr<Lexer> Lex = getLexerStartingFromLoc(Loc, SM, LangOpts); | 
 |   if (!Lex.get()) | 
 |     return SourceLocation(); | 
 |   llvm::SmallVector<char, 16> Line; | 
 |   // FIXME: this is a bit hacky to get ReadToEndOfLine work. | 
 |   Lex->setParsingPreprocessorDirective(true); | 
 |   Lex->ReadToEndOfLine(&Line); | 
 |   auto End = Loc.getLocWithOffset(Line.size()); | 
 |   return SM.getLocForEndOfFile(SM.getDecomposedLoc(Loc).first) == End | 
 |              ? End | 
 |              : End.getLocWithOffset(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); | 
 | } | 
 |  | 
 | void addReplacementOrDie( | 
 |     SourceLocation Start, SourceLocation End, llvm::StringRef ReplacementText, | 
 |     const SourceManager &SM, | 
 |     std::map<std::string, tooling::Replacements> *FileToReplacements) { | 
 |   const auto R = createReplacement(Start, End, ReplacementText, SM); | 
 |   auto Err = (*FileToReplacements)[std::string(R.getFilePath())].add(R); | 
 |   if (Err) | 
 |     llvm_unreachable(llvm::toString(std::move(Err)).c_str()); | 
 | } | 
 |  | 
 | 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. | 
 | // Note that if `DeclName` is `::b::X` and `NsName` is `::a::b`, this returns | 
 | // "::b::X" instead of "b::X" since there will be a name conflict otherwise. | 
 | // \param DeclName A fully qualified name, "::a::b::X" or "a::b::X". | 
 | // \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace | 
 | //        will have empty name. | 
 | std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName, | 
 |                                                 llvm::StringRef NsName) { | 
 |   DeclName = DeclName.ltrim(':'); | 
 |   NsName = NsName.ltrim(':'); | 
 |   if (!DeclName.contains(':')) | 
 |     return std::string(DeclName); | 
 |  | 
 |   auto NsNameSplitted = splitSymbolName(NsName); | 
 |   auto DeclNsSplitted = splitSymbolName(DeclName); | 
 |   llvm::StringRef UnqualifiedDeclName = DeclNsSplitted.pop_back_val(); | 
 |   // If the Decl is in global namespace, there is no need to shorten it. | 
 |   if (DeclNsSplitted.empty()) | 
 |     return std::string(UnqualifiedDeclName); | 
 |   // If NsName is the global namespace, we can simply use the DeclName sans | 
 |   // leading "::". | 
 |   if (NsNameSplitted.empty()) | 
 |     return std::string(DeclName); | 
 |  | 
 |   if (NsNameSplitted.front() != DeclNsSplitted.front()) { | 
 |     // The DeclName must be fully-qualified, but we still need to decide if a | 
 |     // leading "::" is necessary. For example, if `NsName` is "a::b::c" and the | 
 |     // `DeclName` is "b::X", then the reference must be qualified as "::b::X" | 
 |     // to avoid conflict. | 
 |     if (llvm::is_contained(NsNameSplitted, DeclNsSplitted.front())) | 
 |       return ("::" + DeclName).str(); | 
 |     return std::string(DeclName); | 
 |   } | 
 |   // Since there is already an overlap namespace, we know that `DeclName` can be | 
 |   // shortened, so we reduce the longest common prefix. | 
 |   auto DeclI = DeclNsSplitted.begin(); | 
 |   auto DeclE = DeclNsSplitted.end(); | 
 |   auto NsI = NsNameSplitted.begin(); | 
 |   auto NsE = NsNameSplitted.end(); | 
 |   for (; DeclI != DeclE && NsI != NsE && *DeclI == *NsI; ++DeclI, ++NsI) { | 
 |   } | 
 |   return (DeclI == DeclE) | 
 |              ? UnqualifiedDeclName.str() | 
 |              : (llvm::join(DeclI, DeclE, "::") + "::" + UnqualifiedDeclName) | 
 |                    .str(); | 
 | } | 
 |  | 
 | std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) { | 
 |   if (Code.back() != '\n') | 
 |     Code += "\n"; | 
 |   auto NsSplitted = splitSymbolName(NestedNs); | 
 |   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; | 
 | } | 
 |  | 
 | // Returns true if \p D is a nested DeclContext in \p Context | 
 | bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) { | 
 |   while (D) { | 
 |     if (D == Context) | 
 |       return true; | 
 |     D = D->getParent(); | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | // Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx. | 
 | bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D, | 
 |                              const DeclContext *DeclCtx, SourceLocation Loc) { | 
 |   SourceLocation DeclLoc = SM.getSpellingLoc(D->getBeginLoc()); | 
 |   Loc = SM.getSpellingLoc(Loc); | 
 |   return SM.isBeforeInTranslationUnit(DeclLoc, Loc) && | 
 |          (SM.getFileID(DeclLoc) == SM.getFileID(Loc) && | 
 |           isNestedDeclContext(DeclCtx, D->getDeclContext())); | 
 | } | 
 |  | 
 | // Given a qualified symbol name, returns true if the symbol will be | 
 | // incorrectly qualified without leading "::". For example, a symbol | 
 | // "nx::ny::Foo" in namespace "na::nx::ny" without leading "::"; a symbol | 
 | // "util::X" in namespace "na" can potentially conflict with "na::util" (if this | 
 | // exists). | 
 | bool conflictInNamespace(const ASTContext &AST, llvm::StringRef QualifiedSymbol, | 
 |                          llvm::StringRef Namespace) { | 
 |   auto SymbolSplitted = splitSymbolName(QualifiedSymbol.trim(":")); | 
 |   assert(!SymbolSplitted.empty()); | 
 |   SymbolSplitted.pop_back();  // We are only interested in namespaces. | 
 |  | 
 |   if (SymbolSplitted.size() >= 1 && !Namespace.empty()) { | 
 |     auto SymbolTopNs = SymbolSplitted.front(); | 
 |     auto NsSplitted = splitSymbolName(Namespace.trim(":")); | 
 |     assert(!NsSplitted.empty()); | 
 |  | 
 |     auto LookupDecl = [&AST](const Decl &Scope, | 
 |                              llvm::StringRef Name) -> const NamedDecl * { | 
 |       const auto *DC = llvm::dyn_cast<DeclContext>(&Scope); | 
 |       if (!DC) | 
 |         return nullptr; | 
 |       auto LookupRes = DC->lookup(DeclarationName(&AST.Idents.get(Name))); | 
 |       if (LookupRes.empty()) | 
 |         return nullptr; | 
 |       return LookupRes.front(); | 
 |     }; | 
 |     // We do not check the outermost namespace since it would not be a | 
 |     // conflict if it equals to the symbol's outermost namespace and the | 
 |     // symbol name would have been shortened. | 
 |     const NamedDecl *Scope = | 
 |         LookupDecl(*AST.getTranslationUnitDecl(), NsSplitted.front()); | 
 |     for (const auto &I : llvm::drop_begin(NsSplitted)) { | 
 |       if (I == SymbolTopNs) // Handles "::ny" in "::nx::ny" case. | 
 |         return true; | 
 |       // Handles "::util" and "::nx::util" conflicts. | 
 |       if (Scope) { | 
 |         if (LookupDecl(*Scope, SymbolTopNs)) | 
 |           return true; | 
 |         Scope = LookupDecl(*Scope, I); | 
 |       } | 
 |     } | 
 |     if (Scope && LookupDecl(*Scope, SymbolTopNs)) | 
 |       return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool isTemplateParameter(TypeLoc Type) { | 
 |   while (!Type.isNull()) { | 
 |     if (Type.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm) | 
 |       return true; | 
 |     Type = Type.getNextTypeLoc(); | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | } // anonymous namespace | 
 |  | 
 | ChangeNamespaceTool::ChangeNamespaceTool( | 
 |     llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern, | 
 |     llvm::ArrayRef<std::string> AllowedSymbolPatterns, | 
 |     std::map<std::string, tooling::Replacements> *FileToReplacements, | 
 |     llvm::StringRef FallbackStyle) | 
 |     : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements), | 
 |       OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')), | 
 |       FilePattern(FilePattern), FilePatternRE(FilePattern) { | 
 |   FileToReplacements->clear(); | 
 |   auto OldNsSplitted = splitSymbolName(OldNamespace); | 
 |   auto NewNsSplitted = splitSymbolName(NewNamespace); | 
 |   // 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); | 
 |  | 
 |   for (const auto &Pattern : AllowedSymbolPatterns) | 
 |     AllowedSymbolRegexes.emplace_back(Pattern); | 
 | } | 
 |  | 
 | void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) { | 
 |   std::string FullOldNs = "::" + OldNamespace; | 
 |   // Prefix is the outer-most namespace in DiffOldNamespace. For example, if the | 
 |   // OldNamespace is "a::b::c" and DiffOldNamespace is "b::c", then Prefix will | 
 |   // be "a::b". Declarations in this namespace will not be visible in the new | 
 |   // namespace. If DiffOldNamespace is empty, Prefix will be a invalid name "-". | 
 |   llvm::SmallVector<llvm::StringRef, 4> DiffOldNsSplitted; | 
 |   llvm::StringRef(DiffOldNamespace) | 
 |       .split(DiffOldNsSplitted, "::", /*MaxSplit=*/-1, | 
 |              /*KeepEmpty=*/false); | 
 |   std::string Prefix = "-"; | 
 |   if (!DiffOldNsSplitted.empty()) | 
 |     Prefix = (StringRef(FullOldNs).drop_back(DiffOldNamespace.size()) + | 
 |               DiffOldNsSplitted.front()) | 
 |                  .str(); | 
 |   auto IsInMovedNs = | 
 |       allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")), | 
 |             isExpansionInFileMatching(FilePattern)); | 
 |   auto IsVisibleInNewNs = anyOf( | 
 |       IsInMovedNs, unless(hasAncestor(namespaceDecl(hasName(Prefix))))); | 
 |   // Match using declarations. | 
 |   Finder->addMatcher( | 
 |       usingDecl(isExpansionInFileMatching(FilePattern), IsVisibleInNewNs) | 
 |           .bind("using"), | 
 |       this); | 
 |   // Match using namespace declarations. | 
 |   Finder->addMatcher(usingDirectiveDecl(isExpansionInFileMatching(FilePattern), | 
 |                                         IsVisibleInNewNs) | 
 |                          .bind("using_namespace"), | 
 |                      this); | 
 |   // Match namespace alias declarations. | 
 |   Finder->addMatcher(namespaceAliasDecl(isExpansionInFileMatching(FilePattern), | 
 |                                         IsVisibleInNewNs) | 
 |                          .bind("namespace_alias"), | 
 |                      this); | 
 |  | 
 |   // Match old namespace blocks. | 
 |   Finder->addMatcher( | 
 |       namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern)) | 
 |           .bind("old_ns"), | 
 |       this); | 
 |  | 
 |   // Match class forward-declarations in the old namespace. | 
 |   // Note that forward-declarations in classes are not matched. | 
 |   Finder->addMatcher(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())), | 
 |                                    IsInMovedNs, hasParent(namespaceDecl())) | 
 |                          .bind("class_fwd_decl"), | 
 |                      this); | 
 |  | 
 |   // Match template class forward-declarations in the old namespace. | 
 |   Finder->addMatcher( | 
 |       classTemplateDecl(unless(hasDescendant(cxxRecordDecl(isDefinition()))), | 
 |                         IsInMovedNs, hasParent(namespaceDecl())) | 
 |           .bind("template_class_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( | 
 |           isImplicit(), hasAncestor(namespaceDecl(isAnonymous())), | 
 |           hasAncestor(cxxRecordDecl()), | 
 |           allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition()))))))); | 
 |  | 
 |   // Using shadow declarations in classes always refers to base class, which | 
 |   // does not need to be qualified since it can be inferred from inheritance. | 
 |   // Note that this does not match using alias declarations. | 
 |   auto UsingShadowDeclInClass = | 
 |       usingDecl(hasAnyUsingShadowDecl(decl()), hasParent(cxxRecordDecl())); | 
 |  | 
 |   // Match TypeLocs on the declaration. Carefully match only the outermost | 
 |   // TypeLoc and template specialization arguments (which are not outermost) | 
 |   // that are directly linked to types matching `DeclMatcher`. Nested name | 
 |   // specifier locs are handled separately below. | 
 |   Finder->addMatcher( | 
 |       typeLoc(IsInMovedNs, | 
 |               loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))), | 
 |               unless(anyOf(hasParent(typeLoc(loc(qualType( | 
 |                                hasDeclaration(DeclMatcher), | 
 |                                unless(templateSpecializationType()))))), | 
 |                            hasParent(nestedNameSpecifierLoc()), | 
 |                            hasAncestor(decl(isImplicit())), | 
 |                            hasAncestor(UsingShadowDeclInClass), | 
 |                            hasAncestor(functionDecl(isDefaulted())))), | 
 |               hasAncestor(decl().bind("dc"))) | 
 |           .bind("type"), | 
 |       this); | 
 |  | 
 |   // Types in `UsingShadowDecl` is not matched by `typeLoc` above, so we need to | 
 |   // special case it. | 
 |   // Since using declarations inside classes must have the base class in the | 
 |   // nested name specifier, we leave it to the nested name specifier matcher. | 
 |   Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()), | 
 |                                unless(UsingShadowDeclInClass)) | 
 |                          .bind("using_with_shadow"), | 
 |                      this); | 
 |  | 
 |   // Handle types in nested name specifier. Specifiers that are in a TypeLoc | 
 |   // matched above are not matched, e.g. "A::" in "A::A" is not matched since | 
 |   // "A::A" would have already been fixed. | 
 |   Finder->addMatcher( | 
 |       nestedNameSpecifierLoc( | 
 |           hasAncestor(decl(IsInMovedNs).bind("dc")), | 
 |           loc(nestedNameSpecifier( | 
 |               specifiesType(hasDeclaration(DeclMatcher.bind("from_decl"))))), | 
 |           unless(anyOf(hasAncestor(decl(isImplicit())), | 
 |                        hasAncestor(UsingShadowDeclInClass), | 
 |                        hasAncestor(functionDecl(isDefaulted())), | 
 |                        hasAncestor(typeLoc(loc(qualType(hasDeclaration( | 
 |                            decl(equalsBoundNode("from_decl")))))))))) | 
 |           .bind("nested_specifier_loc"), | 
 |       this); | 
 |  | 
 |   // Matches base class initializers in constructors. TypeLocs of base class | 
 |   // initializers do not need to be fixed. For example, | 
 |   //    class X : public a::b::Y { | 
 |   //      public: | 
 |   //        X() : Y::Y() {} // Y::Y do not need namespace specifier. | 
 |   //    }; | 
 |   Finder->addMatcher( | 
 |       cxxCtorInitializer(isBaseInitializer()).bind("base_initializer"), this); | 
 |  | 
 |   // Handle function. | 
 |   // Only handle functions that are defined in a namespace excluding member | 
 |   // function, static methods (qualified by nested specifier), and functions | 
 |   // defined in the global namespace. | 
 |   // Note that the matcher does not exclude calls to out-of-line static method | 
 |   // definitions, so we need to exclude them in the callback handler. | 
 |   auto FuncMatcher = | 
 |       functionDecl(unless(anyOf(cxxMethodDecl(), IsInMovedNs, | 
 |                                 hasAncestor(namespaceDecl(isAnonymous())), | 
 |                                 hasAncestor(cxxRecordDecl()))), | 
 |                    hasParent(namespaceDecl())); | 
 |   Finder->addMatcher(expr(hasAncestor(decl().bind("dc")), IsInMovedNs, | 
 |                           unless(hasAncestor(decl(isImplicit()))), | 
 |                           anyOf(callExpr(callee(FuncMatcher)).bind("call"), | 
 |                                 declRefExpr(to(FuncMatcher.bind("func_decl"))) | 
 |                                     .bind("func_ref"))), | 
 |                      this); | 
 |  | 
 |   auto GlobalVarMatcher = varDecl( | 
 |       hasGlobalStorage(), hasParent(namespaceDecl()), | 
 |       unless(anyOf(IsInMovedNs, hasAncestor(namespaceDecl(isAnonymous()))))); | 
 |   Finder->addMatcher(declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")), | 
 |                                  to(GlobalVarMatcher.bind("var_decl"))) | 
 |                          .bind("var_ref"), | 
 |                      this); | 
 |  | 
 |   // Handle unscoped enum constant. | 
 |   auto UnscopedEnumMatcher = enumConstantDecl(hasParent(enumDecl( | 
 |       hasParent(namespaceDecl()), | 
 |       unless(anyOf(isScoped(), IsInMovedNs, hasAncestor(cxxRecordDecl()), | 
 |                    hasAncestor(namespaceDecl(isAnonymous()))))))); | 
 |   Finder->addMatcher( | 
 |       declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")), | 
 |                   to(UnscopedEnumMatcher.bind("enum_const_decl"))) | 
 |           .bind("enum_const_ref"), | 
 |       this); | 
 | } | 
 |  | 
 | void ChangeNamespaceTool::run( | 
 |     const ast_matchers::MatchFinder::MatchResult &Result) { | 
 |   if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) { | 
 |     UsingDecls.insert(Using); | 
 |   } else if (const auto *UsingNamespace = | 
 |                  Result.Nodes.getNodeAs<UsingDirectiveDecl>( | 
 |                      "using_namespace")) { | 
 |     UsingNamespaceDecls.insert(UsingNamespace); | 
 |   } else if (const auto *NamespaceAlias = | 
 |                  Result.Nodes.getNodeAs<NamespaceAliasDecl>( | 
 |                      "namespace_alias")) { | 
 |     NamespaceAliasDecls.insert(NamespaceAlias); | 
 |   } else if (const auto *NsDecl = | 
 |                  Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) { | 
 |     moveOldNamespace(Result, NsDecl); | 
 |   } else if (const auto *FwdDecl = | 
 |                  Result.Nodes.getNodeAs<CXXRecordDecl>("class_fwd_decl")) { | 
 |     moveClassForwardDeclaration(Result, cast<NamedDecl>(FwdDecl)); | 
 |   } else if (const auto *TemplateFwdDecl = | 
 |                  Result.Nodes.getNodeAs<ClassTemplateDecl>( | 
 |                      "template_class_fwd_decl")) { | 
 |     moveClassForwardDeclaration(Result, cast<NamedDecl>(TemplateFwdDecl)); | 
 |   } else if (const auto *UsingWithShadow = | 
 |                  Result.Nodes.getNodeAs<UsingDecl>("using_with_shadow")) { | 
 |     fixUsingShadowDecl(Result, UsingWithShadow); | 
 |   } else if (const auto *Specifier = | 
 |                  Result.Nodes.getNodeAs<NestedNameSpecifierLoc>( | 
 |                      "nested_specifier_loc")) { | 
 |     SourceLocation Start = Specifier->getBeginLoc(); | 
 |     SourceLocation End = endLocationForType(Specifier->getTypeLoc()); | 
 |     fixTypeLoc(Result, Start, End, Specifier->getTypeLoc()); | 
 |   } else if (const auto *BaseInitializer = | 
 |                  Result.Nodes.getNodeAs<CXXCtorInitializer>( | 
 |                      "base_initializer")) { | 
 |     BaseCtorInitializerTypeLocs.push_back( | 
 |         BaseInitializer->getTypeSourceInfo()->getTypeLoc()); | 
 |   } else if (const auto *TLoc = Result.Nodes.getNodeAs<TypeLoc>("type")) { | 
 |     // This avoids fixing types with record types as qualifier, which is not | 
 |     // filtered by matchers in some cases, e.g. the type is templated. We should | 
 |     // handle the record type qualifier instead. | 
 |     TypeLoc Loc = *TLoc; | 
 |     while (Loc.getTypeLocClass() == TypeLoc::Qualified) | 
 |       Loc = Loc.getNextTypeLoc(); | 
 |     if (Loc.getTypeLocClass() == TypeLoc::Elaborated) { | 
 |       NestedNameSpecifierLoc NestedNameSpecifier = | 
 |           Loc.castAs<ElaboratedTypeLoc>().getQualifierLoc(); | 
 |       // FIXME: avoid changing injected class names. | 
 |       if (auto *NNS = NestedNameSpecifier.getNestedNameSpecifier()) { | 
 |         const Type *SpecifierType = NNS->getAsType(); | 
 |         if (SpecifierType && SpecifierType->isRecordType()) | 
 |           return; | 
 |       } | 
 |     } | 
 |     fixTypeLoc(Result, startLocationForType(Loc), endLocationForType(Loc), Loc); | 
 |   } else if (const auto *VarRef = | 
 |                  Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) { | 
 |     const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl"); | 
 |     assert(Var); | 
 |     if (Var->getCanonicalDecl()->isStaticDataMember()) | 
 |       return; | 
 |     const auto *Context = Result.Nodes.getNodeAs<Decl>("dc"); | 
 |     assert(Context && "Empty decl context."); | 
 |     fixDeclRefExpr(Result, Context->getDeclContext(), | 
 |                    llvm::cast<NamedDecl>(Var), VarRef); | 
 |   } else if (const auto *EnumConstRef = | 
 |                  Result.Nodes.getNodeAs<DeclRefExpr>("enum_const_ref")) { | 
 |     // Do not rename the reference if it is already scoped by the EnumDecl name. | 
 |     if (EnumConstRef->hasQualifier() && | 
 |         EnumConstRef->getQualifier()->getKind() == | 
 |             NestedNameSpecifier::SpecifierKind::TypeSpec && | 
 |         EnumConstRef->getQualifier()->getAsType()->isEnumeralType()) | 
 |       return; | 
 |     const auto *EnumConstDecl = | 
 |         Result.Nodes.getNodeAs<EnumConstantDecl>("enum_const_decl"); | 
 |     assert(EnumConstDecl); | 
 |     const auto *Context = Result.Nodes.getNodeAs<Decl>("dc"); | 
 |     assert(Context && "Empty decl context."); | 
 |     // FIXME: this would qualify "ns::VALUE" as "ns::EnumValue::VALUE". Fix it | 
 |     // if it turns out to be an issue. | 
 |     fixDeclRefExpr(Result, Context->getDeclContext(), | 
 |                    llvm::cast<NamedDecl>(EnumConstDecl), EnumConstRef); | 
 |   } else if (const auto *FuncRef = | 
 |                  Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) { | 
 |     // If this reference has been processed as a function call, we do not | 
 |     // process it again. | 
 |     if (ProcessedFuncRefs.count(FuncRef)) | 
 |       return; | 
 |     ProcessedFuncRefs.insert(FuncRef); | 
 |     const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func_decl"); | 
 |     assert(Func); | 
 |     const auto *Context = Result.Nodes.getNodeAs<Decl>("dc"); | 
 |     assert(Context && "Empty decl context."); | 
 |     fixDeclRefExpr(Result, Context->getDeclContext(), | 
 |                    llvm::cast<NamedDecl>(Func), FuncRef); | 
 |   } else { | 
 |     const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call"); | 
 |     assert(Call != nullptr && "Expecting callback for CallExpr."); | 
 |     const auto *CalleeFuncRef = | 
 |         llvm::cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit()); | 
 |     ProcessedFuncRefs.insert(CalleeFuncRef); | 
 |     const FunctionDecl *Func = Call->getDirectCallee(); | 
 |     assert(Func != nullptr); | 
 |     // FIXME: ignore overloaded operators. This would miss cases where operators | 
 |     // are called by qualified names (i.e. "ns::operator <"). Ignore such | 
 |     // cases for now. | 
 |     if (Func->isOverloadedOperator()) | 
 |       return; | 
 |     // Ignore out-of-line static methods since they will be handled by nested | 
 |     // name specifiers. | 
 |     if (Func->getCanonicalDecl()->getStorageClass() == | 
 |             StorageClass::SC_Static && | 
 |         Func->isOutOfLine()) | 
 |       return; | 
 |     const auto *Context = Result.Nodes.getNodeAs<Decl>("dc"); | 
 |     assert(Context && "Empty decl context."); | 
 |     SourceRange CalleeRange = Call->getCallee()->getSourceRange(); | 
 |     replaceQualifiedSymbolInDeclContext( | 
 |         Result, Context->getDeclContext(), CalleeRange.getBegin(), | 
 |         CalleeRange.getEnd(), llvm::cast<NamedDecl>(Func)); | 
 |   } | 
 | } | 
 |  | 
 | static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl, | 
 |                                                  const SourceManager &SM, | 
 |                                                  const LangOptions &LangOpts) { | 
 |   std::unique_ptr<Lexer> Lex = | 
 |       getLexerStartingFromLoc(NsDecl->getBeginLoc(), SM, LangOpts); | 
 |   assert(Lex.get() && | 
 |          "Failed to create lexer from the beginning of namespace."); | 
 |   if (!Lex.get()) | 
 |     return SourceLocation(); | 
 |   Token Tok; | 
 |   while (!Lex->LexFromRawLexer(Tok) && Tok.isNot(tok::TokenKind::l_brace)) { | 
 |   } | 
 |   return Tok.isNot(tok::TokenKind::l_brace) | 
 |              ? SourceLocation() | 
 |              : Tok.getEndLoc().getLocWithOffset(1); | 
 | } | 
 |  | 
 | // 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; | 
 |  | 
 |   const SourceManager &SM = *Result.SourceManager; | 
 |   // Get the range of the code in the old namespace. | 
 |   SourceLocation Start = | 
 |       getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()); | 
 |   assert(Start.isValid() && "Can't find l_brace for namespace."); | 
 |   MoveNamespace MoveNs; | 
 |   MoveNs.Offset = SM.getFileOffset(Start); | 
 |   // The range of the moved namespace is from the location just past the left | 
 |   // brace to the location right before the right brace. | 
 |   MoveNs.Length = SM.getFileOffset(NsDecl->getRBraceLoc()) - MoveNs.Offset; | 
 |  | 
 |   // 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. | 
 |   // If there is no outer namespace (i.e. DiffOldNamespace is empty), the new | 
 |   // namespace will be a nested namespace in the old namespace. | 
 |   const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace); | 
 |   SourceLocation InsertionLoc = Start; | 
 |   if (OuterNs) { | 
 |     SourceLocation LocAfterNs = getStartOfNextLine( | 
 |         OuterNs->getRBraceLoc(), SM, Result.Context->getLangOpts()); | 
 |     assert(LocAfterNs.isValid() && | 
 |            "Failed to get location after DiffOldNamespace"); | 
 |     InsertionLoc = LocAfterNs; | 
 |   } | 
 |   MoveNs.InsertionOffset = SM.getFileOffset(SM.getSpellingLoc(InsertionLoc)); | 
 |   MoveNs.FID = SM.getFileID(Start); | 
 |   MoveNs.SourceMgr = Result.SourceManager; | 
 |   MoveNamespaces[std::string(SM.getFilename(Start))].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 NamedDecl *FwdDecl) { | 
 |   SourceLocation Start = FwdDecl->getBeginLoc(); | 
 |   SourceLocation End = FwdDecl->getEndLoc(); | 
 |   const SourceManager &SM = *Result.SourceManager; | 
 |   SourceLocation AfterSemi = Lexer::findLocationAfterToken( | 
 |       End, tok::semi, SM, Result.Context->getLangOpts(), | 
 |       /*SkipTrailingWhitespaceAndNewLine=*/true); | 
 |   if (AfterSemi.isValid()) | 
 |     End = AfterSemi.getLocWithOffset(-1); | 
 |   // Delete the forward declaration from the code to be moved. | 
 |   addReplacementOrDie(Start, End, "", SM, &FileToReplacements); | 
 |   llvm::StringRef Code = Lexer::getSourceText( | 
 |       CharSourceRange::getTokenRange(SM.getSpellingLoc(Start), | 
 |                                      SM.getSpellingLoc(End)), | 
 |       SM, 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( | 
 |       getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()), | 
 |       Code, SM); | 
 |   InsertForwardDeclaration InsertFwd; | 
 |   InsertFwd.InsertionOffset = Insertion.getOffset(); | 
 |   InsertFwd.ForwardDeclText = Insertion.getReplacementText().str(); | 
 |   InsertFwdDecls[std::string(Insertion.getFilePath())].push_back(InsertFwd); | 
 | } | 
 |  | 
 | // Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p | 
 | // FromDecl with the shortest qualified name possible when the reference is in | 
 | // `NewNamespace`. | 
 | void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext( | 
 |     const ast_matchers::MatchFinder::MatchResult &Result, | 
 |     const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End, | 
 |     const NamedDecl *FromDecl) { | 
 |   const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext(); | 
 |   if (llvm::isa<TranslationUnitDecl>(NsDeclContext)) { | 
 |     // This should not happen in usual unless the TypeLoc is in function type | 
 |     // parameters, e.g `std::function<void(T)>`. In this case, DeclContext of | 
 |     // `T` will be the translation unit. We simply use fully-qualified name | 
 |     // here. | 
 |     // Note that `FromDecl` must not be defined in the old namespace (according | 
 |     // to `DeclMatcher`), so its fully-qualified name will not change after | 
 |     // changing the namespace. | 
 |     addReplacementOrDie(Start, End, FromDecl->getQualifiedNameAsString(), | 
 |                         *Result.SourceManager, &FileToReplacements); | 
 |     return; | 
 |   } | 
 |   const auto *NsDecl = llvm::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()); | 
 |   std::string FromDeclName = FromDecl->getQualifiedNameAsString(); | 
 |   for (llvm::Regex &RE : AllowedSymbolRegexes) | 
 |     if (RE.match(FromDeclName)) | 
 |       return; | 
 |   std::string ReplaceName = | 
 |       getShortestQualifiedNameInNamespace(FromDeclName, NewNs); | 
 |   // Checks if there is any using namespace declarations that can shorten the | 
 |   // qualified name. | 
 |   for (const auto *UsingNamespace : UsingNamespaceDecls) { | 
 |     if (!isDeclVisibleAtLocation(*Result.SourceManager, UsingNamespace, DeclCtx, | 
 |                                  Start)) | 
 |       continue; | 
 |     StringRef FromDeclNameRef = FromDeclName; | 
 |     if (FromDeclNameRef.consume_front(UsingNamespace->getNominatedNamespace() | 
 |                                           ->getQualifiedNameAsString())) { | 
 |       FromDeclNameRef = FromDeclNameRef.drop_front(2); | 
 |       if (FromDeclNameRef.size() < ReplaceName.size()) | 
 |         ReplaceName = std::string(FromDeclNameRef); | 
 |     } | 
 |   } | 
 |   // Checks if there is any namespace alias declarations that can shorten the | 
 |   // qualified name. | 
 |   for (const auto *NamespaceAlias : NamespaceAliasDecls) { | 
 |     if (!isDeclVisibleAtLocation(*Result.SourceManager, NamespaceAlias, DeclCtx, | 
 |                                  Start)) | 
 |       continue; | 
 |     StringRef FromDeclNameRef = FromDeclName; | 
 |     if (FromDeclNameRef.consume_front( | 
 |             NamespaceAlias->getNamespace()->getQualifiedNameAsString() + | 
 |             "::")) { | 
 |       std::string AliasName = NamespaceAlias->getNameAsString(); | 
 |       std::string AliasQualifiedName = | 
 |           NamespaceAlias->getQualifiedNameAsString(); | 
 |       // We only consider namespace aliases define in the global namespace or | 
 |       // in namespaces that are directly visible from the reference, i.e. | 
 |       // ancestor of the `OldNs`. Note that declarations in ancestor namespaces | 
 |       // but not visible in the new namespace is filtered out by | 
 |       // "IsVisibleInNewNs" matcher. | 
 |       if (AliasQualifiedName != AliasName) { | 
 |         // The alias is defined in some namespace. | 
 |         assert(StringRef(AliasQualifiedName).endswith("::" + AliasName)); | 
 |         llvm::StringRef AliasNs = | 
 |             StringRef(AliasQualifiedName).drop_back(AliasName.size() + 2); | 
 |         if (!llvm::StringRef(OldNs).startswith(AliasNs)) | 
 |           continue; | 
 |       } | 
 |       std::string NameWithAliasNamespace = | 
 |           (AliasName + "::" + FromDeclNameRef).str(); | 
 |       if (NameWithAliasNamespace.size() < ReplaceName.size()) | 
 |         ReplaceName = NameWithAliasNamespace; | 
 |     } | 
 |   } | 
 |   // Checks if there is any using shadow declarations that can shorten the | 
 |   // qualified name. | 
 |   bool Matched = false; | 
 |   for (const UsingDecl *Using : UsingDecls) { | 
 |     if (Matched) | 
 |       break; | 
 |     if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) { | 
 |       for (const auto *UsingShadow : Using->shadows()) { | 
 |         const auto *TargetDecl = UsingShadow->getTargetDecl(); | 
 |         if (TargetDecl->getQualifiedNameAsString() == | 
 |             FromDecl->getQualifiedNameAsString()) { | 
 |           ReplaceName = FromDecl->getNameAsString(); | 
 |           Matched = true; | 
 |           break; | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |   bool Conflict = conflictInNamespace(DeclCtx->getParentASTContext(), | 
 |                                       ReplaceName, NewNamespace); | 
 |   // If the new nested name in the new namespace is the same as it was in the | 
 |   // old namespace, we don't create replacement unless there can be ambiguity. | 
 |   if ((NestedName == ReplaceName && !Conflict) || | 
 |       (NestedName.startswith("::") && NestedName.drop_front(2) == ReplaceName)) | 
 |     return; | 
 |   // If the reference need to be fully-qualified, add a leading "::" unless | 
 |   // NewNamespace is the global namespace. | 
 |   if (ReplaceName == FromDeclName && !NewNamespace.empty() && Conflict) | 
 |     ReplaceName = "::" + ReplaceName; | 
 |   addReplacementOrDie(Start, End, ReplaceName, *Result.SourceManager, | 
 |                       &FileToReplacements); | 
 | } | 
 |  | 
 | // 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; | 
 |   // Types of CXXCtorInitializers do not need to be fixed. | 
 |   if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type)) | 
 |     return; | 
 |   if (isTemplateParameter(Type)) | 
 |     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. | 
 |   auto IsInMovedNs = [&](const NamedDecl *D) { | 
 |     if (!llvm::StringRef(D->getQualifiedNameAsString()) | 
 |              .startswith(OldNamespace + "::")) | 
 |       return false; | 
 |     auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getBeginLoc()); | 
 |     if (ExpansionLoc.isInvalid()) | 
 |       return false; | 
 |     llvm::StringRef Filename = Result.SourceManager->getFilename(ExpansionLoc); | 
 |     return FilePatternRE.match(Filename); | 
 |   }; | 
 |   // Make `FromDecl` the immediate declaration that `Type` refers to, i.e. if | 
 |   // `Type` is an alias type, we make `FromDecl` the type alias declaration. | 
 |   // Also, don't fix the \p Type if it refers to a type alias decl in the moved | 
 |   // namespace since the alias decl will be moved along with the type reference. | 
 |   if (auto *Typedef = Type.getType()->getAs<TypedefType>()) { | 
 |     FromDecl = Typedef->getDecl(); | 
 |     if (IsInMovedNs(FromDecl)) | 
 |       return; | 
 |   } else if (auto *TemplateType = | 
 |                  Type.getType()->getAs<TemplateSpecializationType>()) { | 
 |     if (TemplateType->isTypeAlias()) { | 
 |       FromDecl = TemplateType->getTemplateName().getAsTemplateDecl(); | 
 |       if (IsInMovedNs(FromDecl)) | 
 |         return; | 
 |     } | 
 |   } | 
 |   const auto *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc"); | 
 |   assert(DeclCtx && "Empty decl context."); | 
 |   replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start, | 
 |                                       End, FromDecl); | 
 | } | 
 |  | 
 | void ChangeNamespaceTool::fixUsingShadowDecl( | 
 |     const ast_matchers::MatchFinder::MatchResult &Result, | 
 |     const UsingDecl *UsingDeclaration) { | 
 |   SourceLocation Start = UsingDeclaration->getBeginLoc(); | 
 |   SourceLocation End = UsingDeclaration->getEndLoc(); | 
 |   if (Start.isInvalid() || End.isInvalid()) | 
 |     return; | 
 |  | 
 |   assert(UsingDeclaration->shadow_size() > 0); | 
 |   // FIXME: it might not be always accurate to use the first using-decl. | 
 |   const NamedDecl *TargetDecl = | 
 |       UsingDeclaration->shadow_begin()->getTargetDecl(); | 
 |   std::string TargetDeclName = TargetDecl->getQualifiedNameAsString(); | 
 |   // FIXME: check if target_decl_name is in moved ns, which doesn't make much | 
 |   // sense. If this happens, we need to use name with the new namespace. | 
 |   // Use fully qualified name in UsingDecl for now. | 
 |   addReplacementOrDie(Start, End, "using ::" + TargetDeclName, | 
 |                       *Result.SourceManager, &FileToReplacements); | 
 | } | 
 |  | 
 | void ChangeNamespaceTool::fixDeclRefExpr( | 
 |     const ast_matchers::MatchFinder::MatchResult &Result, | 
 |     const DeclContext *UseContext, const NamedDecl *From, | 
 |     const DeclRefExpr *Ref) { | 
 |   SourceRange RefRange = Ref->getSourceRange(); | 
 |   replaceQualifiedSymbolInDeclContext(Result, UseContext, RefRange.getBegin(), | 
 |                                       RefRange.getEnd(), From); | 
 | } | 
 |  | 
 | 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()->SourceMgr; | 
 |     llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID); | 
 |     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); | 
 |     auto Style = | 
 |         format::getStyle(format::DefaultFormatStyle, FilePath, FallbackStyle); | 
 |     if (!Style) { | 
 |       llvm::errs() << llvm::toString(Style.takeError()) << "\n"; | 
 |       continue; | 
 |     } | 
 |     // 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; | 
 |   } | 
 |  | 
 |   // Make sure we don't generate replacements for files that do not match | 
 |   // FilePattern. | 
 |   for (auto &Entry : FileToReplacements) | 
 |     if (!FilePatternRE.match(Entry.first)) | 
 |       Entry.second.clear(); | 
 | } | 
 |  | 
 | } // namespace change_namespace | 
 | } // namespace clang |