brintos

brintos / llvm-project-archived public Read only

0
0
Text · 44.2 KiB · 471ca45 Raw
1010 lines · cpp
1//===-- ChangeNamespace.cpp - Change namespace implementation -------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8#include "ChangeNamespace.h"9#include "clang/AST/ASTContext.h"10#include "clang/Format/Format.h"11#include "clang/Lex/Lexer.h"12#include "llvm/Support/Casting.h"13#include "llvm/Support/ErrorHandling.h"14 15using namespace clang::ast_matchers;16 17namespace clang {18namespace change_namespace {19 20namespace {21 22inline std::string joinNamespaces(ArrayRef<StringRef> Namespaces) {23  return llvm::join(Namespaces, "::");24}25 26// Given "a::b::c", returns {"a", "b", "c"}.27llvm::SmallVector<llvm::StringRef, 4> splitSymbolName(llvm::StringRef Name) {28  llvm::SmallVector<llvm::StringRef, 4> Splitted;29  Name.split(Splitted, "::", /*MaxSplit=*/-1,30             /*KeepEmpty=*/false);31  return Splitted;32}33 34SourceLocation endLocationForType(TypeLoc TLoc) {35  if (auto QTL = TLoc.getAs<QualifiedTypeLoc>())36    TLoc = QTL.getUnqualifiedLoc();37 38  // The location for template specializations (e.g. Foo<int>) includes the39  // templated types in its location range.  We want to restrict this to just40  // before the `<` character.41  if (TLoc.getTypeLocClass() == TypeLoc::TemplateSpecialization)42    return TLoc.castAs<TemplateSpecializationTypeLoc>()43        .getLAngleLoc()44        .getLocWithOffset(-1);45  return TLoc.getEndLoc();46}47 48// Returns the containing namespace of `InnerNs` by skipping `PartialNsName`.49// If the `InnerNs` does not have `PartialNsName` as suffix, or `PartialNsName`50// is empty, nullptr is returned.51// For example, if `InnerNs` is "a::b::c" and `PartialNsName` is "b::c", then52// the NamespaceDecl of namespace "a" will be returned.53const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs,54                                       llvm::StringRef PartialNsName) {55  if (!InnerNs || PartialNsName.empty())56    return nullptr;57  const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs);58  const auto *CurrentNs = InnerNs;59  auto PartialNsNameSplitted = splitSymbolName(PartialNsName);60  while (!PartialNsNameSplitted.empty()) {61    // Get the inner-most namespace in CurrentContext.62    while (CurrentContext && !llvm::isa<NamespaceDecl>(CurrentContext))63      CurrentContext = CurrentContext->getParent();64    if (!CurrentContext)65      return nullptr;66    CurrentNs = llvm::cast<NamespaceDecl>(CurrentContext);67    if (PartialNsNameSplitted.back() != CurrentNs->getNameAsString())68      return nullptr;69    PartialNsNameSplitted.pop_back();70    CurrentContext = CurrentContext->getParent();71  }72  return CurrentNs;73}74 75static std::unique_ptr<Lexer>76getLexerStartingFromLoc(SourceLocation Loc, const SourceManager &SM,77                        const LangOptions &LangOpts) {78  if (Loc.isMacroID() &&79      !Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc))80    return nullptr;81  // Break down the source location.82  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);83  // Try to load the file buffer.84  bool InvalidTemp = false;85  llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);86  if (InvalidTemp)87    return nullptr;88 89  const char *TokBegin = File.data() + LocInfo.second;90  // Lex from the start of the given location.91  return std::make_unique<Lexer>(SM.getLocForStartOfFile(LocInfo.first),92                                  LangOpts, File.begin(), TokBegin, File.end());93}94 95// FIXME: get rid of this helper function if this is supported in clang-refactor96// library.97static SourceLocation getStartOfNextLine(SourceLocation Loc,98                                         const SourceManager &SM,99                                         const LangOptions &LangOpts) {100  std::unique_ptr<Lexer> Lex = getLexerStartingFromLoc(Loc, SM, LangOpts);101  if (!Lex)102    return SourceLocation();103  llvm::SmallVector<char, 16> Line;104  // FIXME: this is a bit hacky to get ReadToEndOfLine work.105  Lex->setParsingPreprocessorDirective(true);106  Lex->ReadToEndOfLine(&Line);107  auto End = Loc.getLocWithOffset(Line.size());108  return SM.getLocForEndOfFile(SM.getDecomposedLoc(Loc).first) == End109             ? End110             : End.getLocWithOffset(1);111}112 113// Returns `R` with new range that refers to code after `Replaces` being114// applied.115tooling::Replacement116getReplacementInChangedCode(const tooling::Replacements &Replaces,117                            const tooling::Replacement &R) {118  unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset());119  unsigned NewEnd =120      Replaces.getShiftedCodePosition(R.getOffset() + R.getLength());121  return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,122                              R.getReplacementText());123}124 125// Adds a replacement `R` into `Replaces` or merges it into `Replaces` by126// applying all existing Replaces first if there is conflict.127void addOrMergeReplacement(const tooling::Replacement &R,128                           tooling::Replacements *Replaces) {129  auto Err = Replaces->add(R);130  if (Err) {131    llvm::consumeError(std::move(Err));132    auto Replace = getReplacementInChangedCode(*Replaces, R);133    *Replaces = Replaces->merge(tooling::Replacements(Replace));134  }135}136 137tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End,138                                       llvm::StringRef ReplacementText,139                                       const SourceManager &SM) {140  if (!Start.isValid() || !End.isValid()) {141    llvm::errs() << "start or end location were invalid\n";142    return tooling::Replacement();143  }144  if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) {145    llvm::errs()146        << "start or end location were in different macro expansions\n";147    return tooling::Replacement();148  }149  Start = SM.getSpellingLoc(Start);150  End = SM.getSpellingLoc(End);151  if (SM.getFileID(Start) != SM.getFileID(End)) {152    llvm::errs() << "start or end location were in different files\n";153    return tooling::Replacement();154  }155  return tooling::Replacement(156      SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),157                                         SM.getSpellingLoc(End)),158      ReplacementText);159}160 161void addReplacementOrDie(162    SourceLocation Start, SourceLocation End, llvm::StringRef ReplacementText,163    const SourceManager &SM,164    std::map<std::string, tooling::Replacements> *FileToReplacements) {165  const auto R = createReplacement(Start, End, ReplacementText, SM);166  auto Err = (*FileToReplacements)[std::string(R.getFilePath())].add(R);167  if (Err)168    llvm_unreachable(llvm::toString(std::move(Err)).c_str());169}170 171tooling::Replacement createInsertion(SourceLocation Loc,172                                     llvm::StringRef InsertText,173                                     const SourceManager &SM) {174  if (Loc.isInvalid()) {175    llvm::errs() << "insert Location is invalid.\n";176    return tooling::Replacement();177  }178  Loc = SM.getSpellingLoc(Loc);179  return tooling::Replacement(SM, Loc, 0, InsertText);180}181 182// Returns the shortest qualified name for declaration `DeclName` in the183// namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName`184// is "a::c::d", then "b::X" will be returned.185// Note that if `DeclName` is `::b::X` and `NsName` is `::a::b`, this returns186// "::b::X" instead of "b::X" since there will be a name conflict otherwise.187// \param DeclName A fully qualified name, "::a::b::X" or "a::b::X".188// \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace189//        will have empty name.190std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName,191                                                llvm::StringRef NsName) {192  DeclName = DeclName.ltrim(':');193  NsName = NsName.ltrim(':');194  if (!DeclName.contains(':'))195    return std::string(DeclName);196 197  auto NsNameSplitted = splitSymbolName(NsName);198  auto DeclNsSplitted = splitSymbolName(DeclName);199  llvm::StringRef UnqualifiedDeclName = DeclNsSplitted.pop_back_val();200  // If the Decl is in global namespace, there is no need to shorten it.201  if (DeclNsSplitted.empty())202    return std::string(UnqualifiedDeclName);203  // If NsName is the global namespace, we can simply use the DeclName sans204  // leading "::".205  if (NsNameSplitted.empty())206    return std::string(DeclName);207 208  if (NsNameSplitted.front() != DeclNsSplitted.front()) {209    // The DeclName must be fully-qualified, but we still need to decide if a210    // leading "::" is necessary. For example, if `NsName` is "a::b::c" and the211    // `DeclName` is "b::X", then the reference must be qualified as "::b::X"212    // to avoid conflict.213    if (llvm::is_contained(NsNameSplitted, DeclNsSplitted.front()))214      return ("::" + DeclName).str();215    return std::string(DeclName);216  }217  // Since there is already an overlap namespace, we know that `DeclName` can be218  // shortened, so we reduce the longest common prefix.219  auto DeclI = DeclNsSplitted.begin();220  auto DeclE = DeclNsSplitted.end();221  auto NsI = NsNameSplitted.begin();222  auto NsE = NsNameSplitted.end();223  for (; DeclI != DeclE && NsI != NsE && *DeclI == *NsI; ++DeclI, ++NsI) {224  }225  return (DeclI == DeclE)226             ? UnqualifiedDeclName.str()227             : (llvm::join(DeclI, DeclE, "::") + "::" + UnqualifiedDeclName)228                   .str();229}230 231std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) {232  if (Code.back() != '\n')233    Code += "\n";234  auto NsSplitted = splitSymbolName(NestedNs);235  while (!NsSplitted.empty()) {236    // FIXME: consider code style for comments.237    Code = ("namespace " + NsSplitted.back() + " {\n" + Code +238            "} // namespace " + NsSplitted.back() + "\n")239               .str();240    NsSplitted.pop_back();241  }242  return Code;243}244 245// Returns true if \p D is a nested DeclContext in \p Context246bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) {247  while (D) {248    if (D == Context)249      return true;250    D = D->getParent();251  }252  return false;253}254 255// Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx.256bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D,257                             const DeclContext *DeclCtx, SourceLocation Loc) {258  SourceLocation DeclLoc = SM.getSpellingLoc(D->getBeginLoc());259  Loc = SM.getSpellingLoc(Loc);260  return SM.isBeforeInTranslationUnit(DeclLoc, Loc) &&261         (SM.getFileID(DeclLoc) == SM.getFileID(Loc) &&262          isNestedDeclContext(DeclCtx, D->getDeclContext()));263}264 265// Given a qualified symbol name, returns true if the symbol will be266// incorrectly qualified without leading "::". For example, a symbol267// "nx::ny::Foo" in namespace "na::nx::ny" without leading "::"; a symbol268// "util::X" in namespace "na" can potentially conflict with "na::util" (if this269// exists).270bool conflictInNamespace(const ASTContext &AST, llvm::StringRef QualifiedSymbol,271                         llvm::StringRef Namespace) {272  auto SymbolSplitted = splitSymbolName(QualifiedSymbol.trim(":"));273  assert(!SymbolSplitted.empty());274  SymbolSplitted.pop_back();  // We are only interested in namespaces.275 276  if (SymbolSplitted.size() >= 1 && !Namespace.empty()) {277    auto SymbolTopNs = SymbolSplitted.front();278    auto NsSplitted = splitSymbolName(Namespace.trim(":"));279    assert(!NsSplitted.empty());280 281    auto LookupDecl = [&AST](const Decl &Scope,282                             llvm::StringRef Name) -> const NamedDecl * {283      const auto *DC = llvm::dyn_cast<DeclContext>(&Scope);284      if (!DC)285        return nullptr;286      auto LookupRes = DC->lookup(DeclarationName(&AST.Idents.get(Name)));287      if (LookupRes.empty())288        return nullptr;289      return LookupRes.front();290    };291    // We do not check the outermost namespace since it would not be a292    // conflict if it equals to the symbol's outermost namespace and the293    // symbol name would have been shortened.294    const NamedDecl *Scope =295        LookupDecl(*AST.getTranslationUnitDecl(), NsSplitted.front());296    for (const auto &I : llvm::drop_begin(NsSplitted)) {297      if (I == SymbolTopNs) // Handles "::ny" in "::nx::ny" case.298        return true;299      // Handles "::util" and "::nx::util" conflicts.300      if (Scope) {301        if (LookupDecl(*Scope, SymbolTopNs))302          return true;303        Scope = LookupDecl(*Scope, I);304      }305    }306    if (Scope && LookupDecl(*Scope, SymbolTopNs))307      return true;308  }309  return false;310}311 312bool isTemplateParameter(TypeLoc Type) {313  while (!Type.isNull()) {314    if (Type.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm)315      return true;316    Type = Type.getNextTypeLoc();317  }318  return false;319}320 321} // anonymous namespace322 323ChangeNamespaceTool::ChangeNamespaceTool(324    llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,325    llvm::ArrayRef<std::string> AllowedSymbolPatterns,326    std::map<std::string, tooling::Replacements> *FileToReplacements,327    llvm::StringRef FallbackStyle)328    : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements),329      OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')),330      FilePattern(FilePattern), FilePatternRE(FilePattern) {331  FileToReplacements->clear();332  auto OldNsSplitted = splitSymbolName(OldNamespace);333  auto NewNsSplitted = splitSymbolName(NewNamespace);334  // Calculates `DiffOldNamespace` and `DiffNewNamespace`.335  while (!OldNsSplitted.empty() && !NewNsSplitted.empty() &&336         OldNsSplitted.front() == NewNsSplitted.front()) {337    OldNsSplitted.erase(OldNsSplitted.begin());338    NewNsSplitted.erase(NewNsSplitted.begin());339  }340  DiffOldNamespace = joinNamespaces(OldNsSplitted);341  DiffNewNamespace = joinNamespaces(NewNsSplitted);342 343  for (const auto &Pattern : AllowedSymbolPatterns)344    AllowedSymbolRegexes.emplace_back(Pattern);345}346 347void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {348  std::string FullOldNs = "::" + OldNamespace;349  // Prefix is the outer-most namespace in DiffOldNamespace. For example, if the350  // OldNamespace is "a::b::c" and DiffOldNamespace is "b::c", then Prefix will351  // be "a::b". Declarations in this namespace will not be visible in the new352  // namespace. If DiffOldNamespace is empty, Prefix will be a invalid name "-".353  llvm::SmallVector<llvm::StringRef, 4> DiffOldNsSplitted;354  llvm::StringRef(DiffOldNamespace)355      .split(DiffOldNsSplitted, "::", /*MaxSplit=*/-1,356             /*KeepEmpty=*/false);357  std::string Prefix = "-";358  if (!DiffOldNsSplitted.empty())359    Prefix = (StringRef(FullOldNs).drop_back(DiffOldNamespace.size()) +360              DiffOldNsSplitted.front())361                 .str();362  auto IsInMovedNs =363      allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")),364            isExpansionInFileMatching(FilePattern));365  auto IsVisibleInNewNs = anyOf(366      IsInMovedNs, unless(hasAncestor(namespaceDecl(hasName(Prefix)))));367  // Match using declarations.368  Finder->addMatcher(369      usingDecl(isExpansionInFileMatching(FilePattern), IsVisibleInNewNs)370          .bind("using"),371      this);372  // Match using namespace declarations.373  Finder->addMatcher(usingDirectiveDecl(isExpansionInFileMatching(FilePattern),374                                        IsVisibleInNewNs)375                         .bind("using_namespace"),376                     this);377  // Match namespace alias declarations.378  Finder->addMatcher(namespaceAliasDecl(isExpansionInFileMatching(FilePattern),379                                        IsVisibleInNewNs)380                         .bind("namespace_alias"),381                     this);382 383  // Match old namespace blocks.384  Finder->addMatcher(385      namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern))386          .bind("old_ns"),387      this);388 389  // Match class forward-declarations in the old namespace.390  // Note that forward-declarations in classes are not matched.391  Finder->addMatcher(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())),392                                   IsInMovedNs, hasParent(namespaceDecl()))393                         .bind("class_fwd_decl"),394                     this);395 396  // Match template class forward-declarations in the old namespace.397  Finder->addMatcher(398      classTemplateDecl(unless(hasDescendant(cxxRecordDecl(isDefinition()))),399                        IsInMovedNs, hasParent(namespaceDecl()))400          .bind("template_class_fwd_decl"),401      this);402 403  // Match references to types that are not defined in the old namespace.404  // Forward-declarations in the old namespace are also matched since they will405  // be moved back to the old namespace.406  auto DeclMatcher = namedDecl(407      hasAncestor(namespaceDecl()),408      unless(anyOf(409          isImplicit(), hasAncestor(namespaceDecl(isAnonymous())),410          hasAncestor(cxxRecordDecl()),411          allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition())))))));412 413  // Using shadow declarations in classes always refers to base class, which414  // does not need to be qualified since it can be inferred from inheritance.415  // Note that this does not match using alias declarations.416  auto UsingShadowDeclInClass =417      usingDecl(hasAnyUsingShadowDecl(decl()), hasParent(cxxRecordDecl()));418 419  // Match TypeLocs on the declaration. Carefully match only the outermost420  // TypeLoc and template specialization arguments (which are not outermost)421  // that are directly linked to types matching `DeclMatcher`. Nested name422  // specifier locs are handled separately below.423  Finder->addMatcher(424      typeLoc(IsInMovedNs,425              loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))),426              unless(anyOf(hasParent(typeLoc(loc(qualType(427                               hasDeclaration(DeclMatcher),428                               unless(templateSpecializationType()))))),429                           hasParent(nestedNameSpecifierLoc()),430                           hasAncestor(decl(isImplicit())),431                           hasAncestor(UsingShadowDeclInClass),432                           hasAncestor(functionDecl(isDefaulted())))),433              hasAncestor(decl().bind("dc")))434          .bind("type"),435      this);436 437  // Types in `UsingShadowDecl` is not matched by `typeLoc` above, so we need to438  // special case it.439  // Since using declarations inside classes must have the base class in the440  // nested name specifier, we leave it to the nested name specifier matcher.441  Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()),442                               unless(UsingShadowDeclInClass))443                         .bind("using_with_shadow"),444                     this);445 446  // Handle types in nested name specifier. Specifiers that are in a TypeLoc447  // matched above are not matched, e.g. "A::" in "A::A" is not matched since448  // "A::A" would have already been fixed.449  Finder->addMatcher(450      nestedNameSpecifierLoc(451          hasAncestor(decl(IsInMovedNs).bind("dc")),452          loc(nestedNameSpecifier(453              specifiesType(hasDeclaration(DeclMatcher.bind("from_decl"))))),454          unless(anyOf(hasAncestor(decl(isImplicit())),455                       hasAncestor(UsingShadowDeclInClass),456                       hasAncestor(functionDecl(isDefaulted())),457                       hasAncestor(typeLoc(loc(qualType(hasDeclaration(458                           decl(equalsBoundNode("from_decl"))))))))))459          .bind("nested_specifier_loc"),460      this);461 462  // Matches base class initializers in constructors. TypeLocs of base class463  // initializers do not need to be fixed. For example,464  //    class X : public a::b::Y {465  //      public:466  //        X() : Y::Y() {} // Y::Y do not need namespace specifier.467  //    };468  Finder->addMatcher(469      cxxCtorInitializer(isBaseInitializer()).bind("base_initializer"), this);470 471  // Handle function.472  // Only handle functions that are defined in a namespace excluding member473  // function, static methods (qualified by nested specifier), and functions474  // defined in the global namespace.475  // Note that the matcher does not exclude calls to out-of-line static method476  // definitions, so we need to exclude them in the callback handler.477  auto FuncMatcher =478      functionDecl(unless(anyOf(cxxMethodDecl(), IsInMovedNs,479                                hasAncestor(namespaceDecl(isAnonymous())),480                                hasAncestor(cxxRecordDecl()))),481                   hasParent(namespaceDecl()));482  Finder->addMatcher(expr(hasAncestor(decl().bind("dc")), IsInMovedNs,483                          unless(hasAncestor(decl(isImplicit()))),484                          anyOf(callExpr(callee(FuncMatcher)).bind("call"),485                                declRefExpr(to(FuncMatcher.bind("func_decl")))486                                    .bind("func_ref"))),487                     this);488 489  auto GlobalVarMatcher = varDecl(490      hasGlobalStorage(), hasParent(namespaceDecl()),491      unless(anyOf(IsInMovedNs, hasAncestor(namespaceDecl(isAnonymous())))));492  Finder->addMatcher(declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")),493                                 to(GlobalVarMatcher.bind("var_decl")))494                         .bind("var_ref"),495                     this);496 497  // Handle unscoped enum constant.498  auto UnscopedEnumMatcher = enumConstantDecl(hasParent(enumDecl(499      hasParent(namespaceDecl()),500      unless(anyOf(isScoped(), IsInMovedNs, hasAncestor(cxxRecordDecl()),501                   hasAncestor(namespaceDecl(isAnonymous())))))));502  Finder->addMatcher(503      declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")),504                  to(UnscopedEnumMatcher.bind("enum_const_decl")))505          .bind("enum_const_ref"),506      this);507}508 509void ChangeNamespaceTool::run(510    const ast_matchers::MatchFinder::MatchResult &Result) {511  if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) {512    UsingDecls.insert(Using);513  } else if (const auto *UsingNamespace =514                 Result.Nodes.getNodeAs<UsingDirectiveDecl>(515                     "using_namespace")) {516    UsingNamespaceDecls.insert(UsingNamespace);517  } else if (const auto *NamespaceAlias =518                 Result.Nodes.getNodeAs<NamespaceAliasDecl>(519                     "namespace_alias")) {520    NamespaceAliasDecls.insert(NamespaceAlias);521  } else if (const auto *NsDecl =522                 Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) {523    moveOldNamespace(Result, NsDecl);524  } else if (const auto *FwdDecl =525                 Result.Nodes.getNodeAs<CXXRecordDecl>("class_fwd_decl")) {526    moveClassForwardDeclaration(Result, cast<NamedDecl>(FwdDecl));527  } else if (const auto *TemplateFwdDecl =528                 Result.Nodes.getNodeAs<ClassTemplateDecl>(529                     "template_class_fwd_decl")) {530    moveClassForwardDeclaration(Result, cast<NamedDecl>(TemplateFwdDecl));531  } else if (const auto *UsingWithShadow =532                 Result.Nodes.getNodeAs<UsingDecl>("using_with_shadow")) {533    fixUsingShadowDecl(Result, UsingWithShadow);534  } else if (const auto *Specifier =535                 Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(536                     "nested_specifier_loc")) {537    SourceLocation Start = Specifier->getBeginLoc();538    SourceLocation End = endLocationForType(Specifier->castAsTypeLoc());539    fixTypeLoc(Result, Start, End, Specifier->castAsTypeLoc());540  } else if (const auto *BaseInitializer =541                 Result.Nodes.getNodeAs<CXXCtorInitializer>(542                     "base_initializer")) {543    BaseCtorInitializerTypeLocs.push_back(544        BaseInitializer->getTypeSourceInfo()->getTypeLoc());545  } else if (const auto *TLoc = Result.Nodes.getNodeAs<TypeLoc>("type")) {546    // This avoids fixing types with record types as qualifier, which is not547    // filtered by matchers in some cases, e.g. the type is templated. We should548    // handle the record type qualifier instead.549    TypeLoc Loc = *TLoc;550    if (auto QTL = Loc.getAs<QualifiedTypeLoc>())551      Loc = QTL.getUnqualifiedLoc();552    // FIXME: avoid changing injected class names.553    if (NestedNameSpecifier NestedNameSpecifier =554            Loc.getPrefix().getNestedNameSpecifier();555        NestedNameSpecifier.getKind() == NestedNameSpecifier::Kind::Type &&556        NestedNameSpecifier.getAsType()->isRecordType())557      return;558    fixTypeLoc(Result, Loc.getNonElaboratedBeginLoc(), endLocationForType(Loc),559               Loc);560  } else if (const auto *VarRef =561                 Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) {562    const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl");563    assert(Var);564    if (Var->getCanonicalDecl()->isStaticDataMember())565      return;566    const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");567    assert(Context && "Empty decl context.");568    fixDeclRefExpr(Result, Context->getDeclContext(),569                   llvm::cast<NamedDecl>(Var), VarRef);570  } else if (const auto *EnumConstRef =571                 Result.Nodes.getNodeAs<DeclRefExpr>("enum_const_ref")) {572    // Do not rename the reference if it is already scoped by the EnumDecl name.573    if (NestedNameSpecifier Qualifier = EnumConstRef->getQualifier();574        Qualifier.getKind() == NestedNameSpecifier::Kind::Type &&575        Qualifier.getAsType()->isEnumeralType())576      return;577    const auto *EnumConstDecl =578        Result.Nodes.getNodeAs<EnumConstantDecl>("enum_const_decl");579    assert(EnumConstDecl);580    const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");581    assert(Context && "Empty decl context.");582    // FIXME: this would qualify "ns::VALUE" as "ns::EnumValue::VALUE". Fix it583    // if it turns out to be an issue.584    fixDeclRefExpr(Result, Context->getDeclContext(),585                   llvm::cast<NamedDecl>(EnumConstDecl), EnumConstRef);586  } else if (const auto *FuncRef =587                 Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {588    // If this reference has been processed as a function call, we do not589    // process it again.590    if (!ProcessedFuncRefs.insert(FuncRef).second)591      return;592    const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");593    assert(Func);594    const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");595    assert(Context && "Empty decl context.");596    fixDeclRefExpr(Result, Context->getDeclContext(),597                   llvm::cast<NamedDecl>(Func), FuncRef);598  } else {599    const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");600    assert(Call != nullptr && "Expecting callback for CallExpr.");601    const auto *CalleeFuncRef =602        llvm::cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit());603    ProcessedFuncRefs.insert(CalleeFuncRef);604    const FunctionDecl *Func = Call->getDirectCallee();605    assert(Func != nullptr);606    // FIXME: ignore overloaded operators. This would miss cases where operators607    // are called by qualified names (i.e. "ns::operator <"). Ignore such608    // cases for now.609    if (Func->isOverloadedOperator())610      return;611    // Ignore out-of-line static methods since they will be handled by nested612    // name specifiers.613    if (Func->getCanonicalDecl()->getStorageClass() ==614            StorageClass::SC_Static &&615        Func->isOutOfLine())616      return;617    const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");618    assert(Context && "Empty decl context.");619    SourceRange CalleeRange = Call->getCallee()->getSourceRange();620    replaceQualifiedSymbolInDeclContext(621        Result, Context->getDeclContext(), CalleeRange.getBegin(),622        CalleeRange.getEnd(), llvm::cast<NamedDecl>(Func));623  }624}625 626static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl,627                                                 const SourceManager &SM,628                                                 const LangOptions &LangOpts) {629  std::unique_ptr<Lexer> Lex =630      getLexerStartingFromLoc(NsDecl->getBeginLoc(), SM, LangOpts);631  assert(Lex && "Failed to create lexer from the beginning of namespace.");632  if (!Lex)633    return SourceLocation();634  Token Tok;635  while (!Lex->LexFromRawLexer(Tok) && Tok.isNot(tok::TokenKind::l_brace)) {636  }637  return Tok.isNot(tok::TokenKind::l_brace)638             ? SourceLocation()639             : Tok.getEndLoc().getLocWithOffset(1);640}641 642// Stores information about a moved namespace in `MoveNamespaces` and leaves643// the actual movement to `onEndOfTranslationUnit()`.644void ChangeNamespaceTool::moveOldNamespace(645    const ast_matchers::MatchFinder::MatchResult &Result,646    const NamespaceDecl *NsDecl) {647  // If the namespace is empty, do nothing.648  if (Decl::castToDeclContext(NsDecl)->decls_empty())649    return;650 651  const SourceManager &SM = *Result.SourceManager;652  // Get the range of the code in the old namespace.653  SourceLocation Start =654      getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts());655  assert(Start.isValid() && "Can't find l_brace for namespace.");656  MoveNamespace MoveNs;657  MoveNs.Offset = SM.getFileOffset(Start);658  // The range of the moved namespace is from the location just past the left659  // brace to the location right before the right brace.660  MoveNs.Length = SM.getFileOffset(NsDecl->getRBraceLoc()) - MoveNs.Offset;661 662  // Insert the new namespace after `DiffOldNamespace`. For example, if663  // `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then664  // "x::y" will be inserted inside the existing namespace "a" and after "a::b".665  // `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b"666  // in the above example.667  // If there is no outer namespace (i.e. DiffOldNamespace is empty), the new668  // namespace will be a nested namespace in the old namespace.669  const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace);670  SourceLocation InsertionLoc = Start;671  if (OuterNs) {672    SourceLocation LocAfterNs = getStartOfNextLine(673        OuterNs->getRBraceLoc(), SM, Result.Context->getLangOpts());674    assert(LocAfterNs.isValid() &&675           "Failed to get location after DiffOldNamespace");676    InsertionLoc = LocAfterNs;677  }678  MoveNs.InsertionOffset = SM.getFileOffset(SM.getSpellingLoc(InsertionLoc));679  MoveNs.FID = SM.getFileID(Start);680  MoveNs.SourceMgr = Result.SourceManager;681  MoveNamespaces[std::string(SM.getFilename(Start))].push_back(MoveNs);682}683 684// Removes a class forward declaration from the code in the moved namespace and685// creates an `InsertForwardDeclaration` to insert the forward declaration back686// into the old namespace after moving code from the old namespace to the new687// namespace.688// For example, changing "a" to "x":689// Old code:690//   namespace a {691//   class FWD;692//   class A { FWD *fwd; }693//   }  // a694// New code:695//   namespace a {696//   class FWD;697//   }  // a698//   namespace x {699//   class A { a::FWD *fwd; }700//   }  // x701void ChangeNamespaceTool::moveClassForwardDeclaration(702    const ast_matchers::MatchFinder::MatchResult &Result,703    const NamedDecl *FwdDecl) {704  SourceLocation Start = FwdDecl->getBeginLoc();705  SourceLocation End = FwdDecl->getEndLoc();706  const SourceManager &SM = *Result.SourceManager;707  SourceLocation AfterSemi = Lexer::findLocationAfterToken(708      End, tok::semi, SM, Result.Context->getLangOpts(),709      /*SkipTrailingWhitespaceAndNewLine=*/true);710  if (AfterSemi.isValid())711    End = AfterSemi.getLocWithOffset(-1);712  // Delete the forward declaration from the code to be moved.713  addReplacementOrDie(Start, End, "", SM, &FileToReplacements);714  llvm::StringRef Code = Lexer::getSourceText(715      CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),716                                     SM.getSpellingLoc(End)),717      SM, Result.Context->getLangOpts());718  // Insert the forward declaration back into the old namespace after moving the719  // code from old namespace to new namespace.720  // Insertion information is stored in `InsertFwdDecls` and actual721  // insertion will be performed in `onEndOfTranslationUnit`.722  // Get the (old) namespace that contains the forward declaration.723  const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl");724  // The namespace contains the forward declaration, so it must not be empty.725  assert(!NsDecl->decls_empty());726  const auto Insertion = createInsertion(727      getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()),728      Code, SM);729  InsertForwardDeclaration InsertFwd;730  InsertFwd.InsertionOffset = Insertion.getOffset();731  InsertFwd.ForwardDeclText = Insertion.getReplacementText().str();732  InsertFwdDecls[std::string(Insertion.getFilePath())].push_back(InsertFwd);733}734 735// Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p736// FromDecl with the shortest qualified name possible when the reference is in737// `NewNamespace`.738void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(739    const ast_matchers::MatchFinder::MatchResult &Result,740    const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End,741    const NamedDecl *FromDecl) {742  const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext();743  if (llvm::isa<TranslationUnitDecl>(NsDeclContext)) {744    // This should not happen in usual unless the TypeLoc is in function type745    // parameters, e.g `std::function<void(T)>`. In this case, DeclContext of746    // `T` will be the translation unit. We simply use fully-qualified name747    // here.748    // Note that `FromDecl` must not be defined in the old namespace (according749    // to `DeclMatcher`), so its fully-qualified name will not change after750    // changing the namespace.751    addReplacementOrDie(Start, End, FromDecl->getQualifiedNameAsString(),752                        *Result.SourceManager, &FileToReplacements);753    return;754  }755  const auto *NsDecl = llvm::cast<NamespaceDecl>(NsDeclContext);756  // Calculate the name of the `NsDecl` after it is moved to new namespace.757  std::string OldNs = NsDecl->getQualifiedNameAsString();758  llvm::StringRef Postfix = OldNs;759  bool Consumed = Postfix.consume_front(OldNamespace);760  assert(Consumed && "Expect OldNS to start with OldNamespace.");761  (void)Consumed;762  const std::string NewNs = (NewNamespace + Postfix).str();763 764  llvm::StringRef NestedName = Lexer::getSourceText(765      CharSourceRange::getTokenRange(766          Result.SourceManager->getSpellingLoc(Start),767          Result.SourceManager->getSpellingLoc(End)),768      *Result.SourceManager, Result.Context->getLangOpts());769  std::string FromDeclName = FromDecl->getQualifiedNameAsString();770  for (llvm::Regex &RE : AllowedSymbolRegexes)771    if (RE.match(FromDeclName))772      return;773  std::string ReplaceName =774      getShortestQualifiedNameInNamespace(FromDeclName, NewNs);775  // Checks if there is any using namespace declarations that can shorten the776  // qualified name.777  for (const auto *UsingNamespace : UsingNamespaceDecls) {778    if (!isDeclVisibleAtLocation(*Result.SourceManager, UsingNamespace, DeclCtx,779                                 Start))780      continue;781    StringRef FromDeclNameRef = FromDeclName;782    if (FromDeclNameRef.consume_front(UsingNamespace->getNominatedNamespace()783                                          ->getQualifiedNameAsString())) {784      FromDeclNameRef = FromDeclNameRef.drop_front(2);785      if (FromDeclNameRef.size() < ReplaceName.size())786        ReplaceName = std::string(FromDeclNameRef);787    }788  }789  // Checks if there is any namespace alias declarations that can shorten the790  // qualified name.791  for (const auto *NamespaceAlias : NamespaceAliasDecls) {792    if (!isDeclVisibleAtLocation(*Result.SourceManager, NamespaceAlias, DeclCtx,793                                 Start))794      continue;795    StringRef FromDeclNameRef = FromDeclName;796    if (FromDeclNameRef.consume_front(797            NamespaceAlias->getNamespace()->getQualifiedNameAsString() +798            "::")) {799      std::string AliasName = NamespaceAlias->getNameAsString();800      std::string AliasQualifiedName =801          NamespaceAlias->getQualifiedNameAsString();802      // We only consider namespace aliases define in the global namespace or803      // in namespaces that are directly visible from the reference, i.e.804      // ancestor of the `OldNs`. Note that declarations in ancestor namespaces805      // but not visible in the new namespace is filtered out by806      // "IsVisibleInNewNs" matcher.807      if (AliasQualifiedName != AliasName) {808        // The alias is defined in some namespace.809        assert(StringRef(AliasQualifiedName).ends_with("::" + AliasName));810        llvm::StringRef AliasNs =811            StringRef(AliasQualifiedName).drop_back(AliasName.size() + 2);812        if (!llvm::StringRef(OldNs).starts_with(AliasNs))813          continue;814      }815      std::string NameWithAliasNamespace =816          (AliasName + "::" + FromDeclNameRef).str();817      if (NameWithAliasNamespace.size() < ReplaceName.size())818        ReplaceName = NameWithAliasNamespace;819    }820  }821  // Checks if there is any using shadow declarations that can shorten the822  // qualified name.823  bool Matched = false;824  for (const UsingDecl *Using : UsingDecls) {825    if (Matched)826      break;827    if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) {828      for (const auto *UsingShadow : Using->shadows()) {829        const auto *TargetDecl = UsingShadow->getTargetDecl();830        if (TargetDecl->getQualifiedNameAsString() ==831            FromDecl->getQualifiedNameAsString()) {832          ReplaceName = FromDecl->getNameAsString();833          Matched = true;834          break;835        }836      }837    }838  }839  bool Conflict = conflictInNamespace(DeclCtx->getParentASTContext(),840                                      ReplaceName, NewNamespace);841  // If the new nested name in the new namespace is the same as it was in the842  // old namespace, we don't create replacement unless there can be ambiguity.843  if ((NestedName == ReplaceName && !Conflict) ||844      (NestedName.starts_with("::") && NestedName.drop_front(2) == ReplaceName))845    return;846  // If the reference need to be fully-qualified, add a leading "::" unless847  // NewNamespace is the global namespace.848  if (ReplaceName == FromDeclName && !NewNamespace.empty() && Conflict)849    ReplaceName = "::" + ReplaceName;850  addReplacementOrDie(Start, End, ReplaceName, *Result.SourceManager,851                      &FileToReplacements);852}853 854// Replace the [Start, End] of `Type` with the shortest qualified name when the855// `Type` is in `NewNamespace`.856void ChangeNamespaceTool::fixTypeLoc(857    const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start,858    SourceLocation End, TypeLoc Type) {859  // FIXME: do not rename template parameter.860  if (Start.isInvalid() || End.isInvalid())861    return;862  // Types of CXXCtorInitializers do not need to be fixed.863  if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type))864    return;865  if (isTemplateParameter(Type))866    return;867  // The declaration which this TypeLoc refers to.868  const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl");869  // `hasDeclaration` gives underlying declaration, but if the type is870  // a typedef type, we need to use the typedef type instead.871  auto IsInMovedNs = [&](const NamedDecl *D) {872    if (!llvm::StringRef(D->getQualifiedNameAsString())873             .starts_with(OldNamespace + "::"))874      return false;875    auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getBeginLoc());876    if (ExpansionLoc.isInvalid())877      return false;878    llvm::StringRef Filename = Result.SourceManager->getFilename(ExpansionLoc);879    return FilePatternRE.match(Filename);880  };881  // Make `FromDecl` the immediate declaration that `Type` refers to, i.e. if882  // `Type` is an alias type, we make `FromDecl` the type alias declaration.883  // Also, don't fix the \p Type if it refers to a type alias decl in the moved884  // namespace since the alias decl will be moved along with the type reference.885  if (auto *Typedef = Type.getType()->getAs<TypedefType>()) {886    FromDecl = Typedef->getDecl();887    if (IsInMovedNs(FromDecl))888      return;889  } else if (auto *TemplateType =890                 Type.getType()->getAs<TemplateSpecializationType>()) {891    if (TemplateType->isTypeAlias()) {892      FromDecl = TemplateType->getTemplateName().getAsTemplateDecl();893      if (IsInMovedNs(FromDecl))894        return;895    }896  }897  const auto *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc");898  assert(DeclCtx && "Empty decl context.");899  replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start,900                                      End, FromDecl);901}902 903void ChangeNamespaceTool::fixUsingShadowDecl(904    const ast_matchers::MatchFinder::MatchResult &Result,905    const UsingDecl *UsingDeclaration) {906  SourceLocation Start = UsingDeclaration->getBeginLoc();907  SourceLocation End = UsingDeclaration->getEndLoc();908  if (Start.isInvalid() || End.isInvalid())909    return;910 911  assert(UsingDeclaration->shadow_size() > 0);912  // FIXME: it might not be always accurate to use the first using-decl.913  const NamedDecl *TargetDecl =914      UsingDeclaration->shadow_begin()->getTargetDecl();915  std::string TargetDeclName = TargetDecl->getQualifiedNameAsString();916  // FIXME: check if target_decl_name is in moved ns, which doesn't make much917  // sense. If this happens, we need to use name with the new namespace.918  // Use fully qualified name in UsingDecl for now.919  addReplacementOrDie(Start, End, "using ::" + TargetDeclName,920                      *Result.SourceManager, &FileToReplacements);921}922 923void ChangeNamespaceTool::fixDeclRefExpr(924    const ast_matchers::MatchFinder::MatchResult &Result,925    const DeclContext *UseContext, const NamedDecl *From,926    const DeclRefExpr *Ref) {927  SourceRange RefRange = Ref->getSourceRange();928  replaceQualifiedSymbolInDeclContext(Result, UseContext, RefRange.getBegin(),929                                      RefRange.getEnd(), From);930}931 932void ChangeNamespaceTool::onEndOfTranslationUnit() {933  // Move namespace blocks and insert forward declaration to old namespace.934  for (const auto &FileAndNsMoves : MoveNamespaces) {935    auto &NsMoves = FileAndNsMoves.second;936    if (NsMoves.empty())937      continue;938    const std::string &FilePath = FileAndNsMoves.first;939    auto &Replaces = FileToReplacements[FilePath];940    auto &SM = *NsMoves.begin()->SourceMgr;941    llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID);942    auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);943    if (!ChangedCode) {944      llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";945      continue;946    }947    // Replacements on the changed code for moving namespaces and inserting948    // forward declarations to old namespaces.949    tooling::Replacements NewReplacements;950    // Cut the changed code from the old namespace and paste the code in the new951    // namespace.952    for (const auto &NsMove : NsMoves) {953      // Calculate the range of the old namespace block in the changed954      // code.955      const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset);956      const unsigned NewLength =957          Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) -958          NewOffset;959      tooling::Replacement Deletion(FilePath, NewOffset, NewLength, "");960      std::string MovedCode = ChangedCode->substr(NewOffset, NewLength);961      std::string MovedCodeWrappedInNewNs =962          wrapCodeInNamespace(DiffNewNamespace, MovedCode);963      // Calculate the new offset at which the code will be inserted in the964      // changed code.965      unsigned NewInsertionOffset =966          Replaces.getShiftedCodePosition(NsMove.InsertionOffset);967      tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,968                                     MovedCodeWrappedInNewNs);969      addOrMergeReplacement(Deletion, &NewReplacements);970      addOrMergeReplacement(Insertion, &NewReplacements);971    }972    // After moving namespaces, insert forward declarations back to old973    // namespaces.974    const auto &FwdDeclInsertions = InsertFwdDecls[FilePath];975    for (const auto &FwdDeclInsertion : FwdDeclInsertions) {976      unsigned NewInsertionOffset =977          Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset);978      tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,979                                     FwdDeclInsertion.ForwardDeclText);980      addOrMergeReplacement(Insertion, &NewReplacements);981    }982    // Add replacements referring to the changed code to existing replacements,983    // which refers to the original code.984    Replaces = Replaces.merge(NewReplacements);985    auto Style =986        format::getStyle(format::DefaultFormatStyle, FilePath, FallbackStyle);987    if (!Style) {988      llvm::errs() << llvm::toString(Style.takeError()) << "\n";989      continue;990    }991    // Clean up old namespaces if there is nothing in it after moving.992    auto CleanReplacements =993        format::cleanupAroundReplacements(Code, Replaces, *Style);994    if (!CleanReplacements) {995      llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";996      continue;997    }998    FileToReplacements[FilePath] = *CleanReplacements;999  }1000 1001  // Make sure we don't generate replacements for files that do not match1002  // FilePattern.1003  for (auto &Entry : FileToReplacements)1004    if (!FilePatternRE.match(Entry.first))1005      Entry.second.clear();1006}1007 1008} // namespace change_namespace1009} // namespace clang1010