15f5a7458SNico Weber //===-- ChangeNamespace.cpp - Change namespace implementation -------------===// 25f5a7458SNico Weber // 35f5a7458SNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45f5a7458SNico Weber // See https://llvm.org/LICENSE.txt for license information. 55f5a7458SNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65f5a7458SNico Weber // 75f5a7458SNico Weber //===----------------------------------------------------------------------===// 85f5a7458SNico Weber #include "ChangeNamespace.h" 95f5a7458SNico Weber #include "clang/AST/ASTContext.h" 105f5a7458SNico Weber #include "clang/Format/Format.h" 115f5a7458SNico Weber #include "clang/Lex/Lexer.h" 125f5a7458SNico Weber #include "llvm/Support/Casting.h" 135f5a7458SNico Weber #include "llvm/Support/ErrorHandling.h" 145f5a7458SNico Weber 155f5a7458SNico Weber using namespace clang::ast_matchers; 165f5a7458SNico Weber 175f5a7458SNico Weber namespace clang { 185f5a7458SNico Weber namespace change_namespace { 195f5a7458SNico Weber 205f5a7458SNico Weber namespace { 215f5a7458SNico Weber 226a3b10e2SNathan James inline std::string joinNamespaces(ArrayRef<StringRef> Namespaces) { 236a3b10e2SNathan James return llvm::join(Namespaces, "::"); 245f5a7458SNico Weber } 255f5a7458SNico Weber 265f5a7458SNico Weber // Given "a::b::c", returns {"a", "b", "c"}. 275f5a7458SNico Weber llvm::SmallVector<llvm::StringRef, 4> splitSymbolName(llvm::StringRef Name) { 285f5a7458SNico Weber llvm::SmallVector<llvm::StringRef, 4> Splitted; 295f5a7458SNico Weber Name.split(Splitted, "::", /*MaxSplit=*/-1, 305f5a7458SNico Weber /*KeepEmpty=*/false); 315f5a7458SNico Weber return Splitted; 325f5a7458SNico Weber } 335f5a7458SNico Weber 345f5a7458SNico Weber SourceLocation startLocationForType(TypeLoc TLoc) { 355f5a7458SNico Weber // For elaborated types (e.g. `struct a::A`) we want the portion after the 365f5a7458SNico Weber // `struct` but including the namespace qualifier, `a::`. 375f5a7458SNico Weber if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) { 385f5a7458SNico Weber NestedNameSpecifierLoc NestedNameSpecifier = 395f5a7458SNico Weber TLoc.castAs<ElaboratedTypeLoc>().getQualifierLoc(); 405f5a7458SNico Weber if (NestedNameSpecifier.getNestedNameSpecifier()) 415f5a7458SNico Weber return NestedNameSpecifier.getBeginLoc(); 425f5a7458SNico Weber TLoc = TLoc.getNextTypeLoc(); 435f5a7458SNico Weber } 445f5a7458SNico Weber return TLoc.getBeginLoc(); 455f5a7458SNico Weber } 465f5a7458SNico Weber 475f5a7458SNico Weber SourceLocation endLocationForType(TypeLoc TLoc) { 485f5a7458SNico Weber // Dig past any namespace or keyword qualifications. 495f5a7458SNico Weber while (TLoc.getTypeLocClass() == TypeLoc::Elaborated || 505f5a7458SNico Weber TLoc.getTypeLocClass() == TypeLoc::Qualified) 515f5a7458SNico Weber TLoc = TLoc.getNextTypeLoc(); 525f5a7458SNico Weber 535f5a7458SNico Weber // The location for template specializations (e.g. Foo<int>) includes the 545f5a7458SNico Weber // templated types in its location range. We want to restrict this to just 555f5a7458SNico Weber // before the `<` character. 565f5a7458SNico Weber if (TLoc.getTypeLocClass() == TypeLoc::TemplateSpecialization) 575f5a7458SNico Weber return TLoc.castAs<TemplateSpecializationTypeLoc>() 585f5a7458SNico Weber .getLAngleLoc() 595f5a7458SNico Weber .getLocWithOffset(-1); 605f5a7458SNico Weber return TLoc.getEndLoc(); 615f5a7458SNico Weber } 625f5a7458SNico Weber 635f5a7458SNico Weber // Returns the containing namespace of `InnerNs` by skipping `PartialNsName`. 645f5a7458SNico Weber // If the `InnerNs` does not have `PartialNsName` as suffix, or `PartialNsName` 655f5a7458SNico Weber // is empty, nullptr is returned. 665f5a7458SNico Weber // For example, if `InnerNs` is "a::b::c" and `PartialNsName` is "b::c", then 675f5a7458SNico Weber // the NamespaceDecl of namespace "a" will be returned. 685f5a7458SNico Weber const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs, 695f5a7458SNico Weber llvm::StringRef PartialNsName) { 705f5a7458SNico Weber if (!InnerNs || PartialNsName.empty()) 715f5a7458SNico Weber return nullptr; 725f5a7458SNico Weber const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs); 735f5a7458SNico Weber const auto *CurrentNs = InnerNs; 745f5a7458SNico Weber auto PartialNsNameSplitted = splitSymbolName(PartialNsName); 755f5a7458SNico Weber while (!PartialNsNameSplitted.empty()) { 765f5a7458SNico Weber // Get the inner-most namespace in CurrentContext. 775f5a7458SNico Weber while (CurrentContext && !llvm::isa<NamespaceDecl>(CurrentContext)) 785f5a7458SNico Weber CurrentContext = CurrentContext->getParent(); 795f5a7458SNico Weber if (!CurrentContext) 805f5a7458SNico Weber return nullptr; 815f5a7458SNico Weber CurrentNs = llvm::cast<NamespaceDecl>(CurrentContext); 825f5a7458SNico Weber if (PartialNsNameSplitted.back() != CurrentNs->getNameAsString()) 835f5a7458SNico Weber return nullptr; 845f5a7458SNico Weber PartialNsNameSplitted.pop_back(); 855f5a7458SNico Weber CurrentContext = CurrentContext->getParent(); 865f5a7458SNico Weber } 875f5a7458SNico Weber return CurrentNs; 885f5a7458SNico Weber } 895f5a7458SNico Weber 905f5a7458SNico Weber static std::unique_ptr<Lexer> 915f5a7458SNico Weber getLexerStartingFromLoc(SourceLocation Loc, const SourceManager &SM, 925f5a7458SNico Weber const LangOptions &LangOpts) { 935f5a7458SNico Weber if (Loc.isMacroID() && 945f5a7458SNico Weber !Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc)) 955f5a7458SNico Weber return nullptr; 965f5a7458SNico Weber // Break down the source location. 975f5a7458SNico Weber std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 985f5a7458SNico Weber // Try to load the file buffer. 995f5a7458SNico Weber bool InvalidTemp = false; 1005f5a7458SNico Weber llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp); 1015f5a7458SNico Weber if (InvalidTemp) 1025f5a7458SNico Weber return nullptr; 1035f5a7458SNico Weber 1045f5a7458SNico Weber const char *TokBegin = File.data() + LocInfo.second; 1055f5a7458SNico Weber // Lex from the start of the given location. 1061c705d9cSJonas Devlieghere return std::make_unique<Lexer>(SM.getLocForStartOfFile(LocInfo.first), 1075f5a7458SNico Weber LangOpts, File.begin(), TokBegin, File.end()); 1085f5a7458SNico Weber } 1095f5a7458SNico Weber 1105f5a7458SNico Weber // FIXME: get rid of this helper function if this is supported in clang-refactor 1115f5a7458SNico Weber // library. 1125f5a7458SNico Weber static SourceLocation getStartOfNextLine(SourceLocation Loc, 1135f5a7458SNico Weber const SourceManager &SM, 1145f5a7458SNico Weber const LangOptions &LangOpts) { 1155f5a7458SNico Weber std::unique_ptr<Lexer> Lex = getLexerStartingFromLoc(Loc, SM, LangOpts); 1165f5a7458SNico Weber if (!Lex.get()) 1175f5a7458SNico Weber return SourceLocation(); 1185f5a7458SNico Weber llvm::SmallVector<char, 16> Line; 1195f5a7458SNico Weber // FIXME: this is a bit hacky to get ReadToEndOfLine work. 1205f5a7458SNico Weber Lex->setParsingPreprocessorDirective(true); 1215f5a7458SNico Weber Lex->ReadToEndOfLine(&Line); 1225f5a7458SNico Weber auto End = Loc.getLocWithOffset(Line.size()); 1235f5a7458SNico Weber return SM.getLocForEndOfFile(SM.getDecomposedLoc(Loc).first) == End 1245f5a7458SNico Weber ? End 1255f5a7458SNico Weber : End.getLocWithOffset(1); 1265f5a7458SNico Weber } 1275f5a7458SNico Weber 1285f5a7458SNico Weber // Returns `R` with new range that refers to code after `Replaces` being 1295f5a7458SNico Weber // applied. 1305f5a7458SNico Weber tooling::Replacement 1315f5a7458SNico Weber getReplacementInChangedCode(const tooling::Replacements &Replaces, 1325f5a7458SNico Weber const tooling::Replacement &R) { 1335f5a7458SNico Weber unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset()); 1345f5a7458SNico Weber unsigned NewEnd = 1355f5a7458SNico Weber Replaces.getShiftedCodePosition(R.getOffset() + R.getLength()); 1365f5a7458SNico Weber return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart, 1375f5a7458SNico Weber R.getReplacementText()); 1385f5a7458SNico Weber } 1395f5a7458SNico Weber 1405f5a7458SNico Weber // Adds a replacement `R` into `Replaces` or merges it into `Replaces` by 1415f5a7458SNico Weber // applying all existing Replaces first if there is conflict. 1425f5a7458SNico Weber void addOrMergeReplacement(const tooling::Replacement &R, 1435f5a7458SNico Weber tooling::Replacements *Replaces) { 1445f5a7458SNico Weber auto Err = Replaces->add(R); 1455f5a7458SNico Weber if (Err) { 1465f5a7458SNico Weber llvm::consumeError(std::move(Err)); 1475f5a7458SNico Weber auto Replace = getReplacementInChangedCode(*Replaces, R); 1485f5a7458SNico Weber *Replaces = Replaces->merge(tooling::Replacements(Replace)); 1495f5a7458SNico Weber } 1505f5a7458SNico Weber } 1515f5a7458SNico Weber 1525f5a7458SNico Weber tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End, 1535f5a7458SNico Weber llvm::StringRef ReplacementText, 1545f5a7458SNico Weber const SourceManager &SM) { 1555f5a7458SNico Weber if (!Start.isValid() || !End.isValid()) { 1565f5a7458SNico Weber llvm::errs() << "start or end location were invalid\n"; 1575f5a7458SNico Weber return tooling::Replacement(); 1585f5a7458SNico Weber } 1595f5a7458SNico Weber if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) { 1605f5a7458SNico Weber llvm::errs() 1615f5a7458SNico Weber << "start or end location were in different macro expansions\n"; 1625f5a7458SNico Weber return tooling::Replacement(); 1635f5a7458SNico Weber } 1645f5a7458SNico Weber Start = SM.getSpellingLoc(Start); 1655f5a7458SNico Weber End = SM.getSpellingLoc(End); 1665f5a7458SNico Weber if (SM.getFileID(Start) != SM.getFileID(End)) { 1675f5a7458SNico Weber llvm::errs() << "start or end location were in different files\n"; 1685f5a7458SNico Weber return tooling::Replacement(); 1695f5a7458SNico Weber } 1705f5a7458SNico Weber return tooling::Replacement( 1715f5a7458SNico Weber SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start), 1725f5a7458SNico Weber SM.getSpellingLoc(End)), 1735f5a7458SNico Weber ReplacementText); 1745f5a7458SNico Weber } 1755f5a7458SNico Weber 1765f5a7458SNico Weber void addReplacementOrDie( 1775f5a7458SNico Weber SourceLocation Start, SourceLocation End, llvm::StringRef ReplacementText, 1785f5a7458SNico Weber const SourceManager &SM, 1795f5a7458SNico Weber std::map<std::string, tooling::Replacements> *FileToReplacements) { 1805f5a7458SNico Weber const auto R = createReplacement(Start, End, ReplacementText, SM); 181adcd0268SBenjamin Kramer auto Err = (*FileToReplacements)[std::string(R.getFilePath())].add(R); 1825f5a7458SNico Weber if (Err) 1835f5a7458SNico Weber llvm_unreachable(llvm::toString(std::move(Err)).c_str()); 1845f5a7458SNico Weber } 1855f5a7458SNico Weber 1865f5a7458SNico Weber tooling::Replacement createInsertion(SourceLocation Loc, 1875f5a7458SNico Weber llvm::StringRef InsertText, 1885f5a7458SNico Weber const SourceManager &SM) { 1895f5a7458SNico Weber if (Loc.isInvalid()) { 1905f5a7458SNico Weber llvm::errs() << "insert Location is invalid.\n"; 1915f5a7458SNico Weber return tooling::Replacement(); 1925f5a7458SNico Weber } 1935f5a7458SNico Weber Loc = SM.getSpellingLoc(Loc); 1945f5a7458SNico Weber return tooling::Replacement(SM, Loc, 0, InsertText); 1955f5a7458SNico Weber } 1965f5a7458SNico Weber 1975f5a7458SNico Weber // Returns the shortest qualified name for declaration `DeclName` in the 1985f5a7458SNico Weber // namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName` 1995f5a7458SNico Weber // is "a::c::d", then "b::X" will be returned. 2005f5a7458SNico Weber // Note that if `DeclName` is `::b::X` and `NsName` is `::a::b`, this returns 2015f5a7458SNico Weber // "::b::X" instead of "b::X" since there will be a name conflict otherwise. 2025f5a7458SNico Weber // \param DeclName A fully qualified name, "::a::b::X" or "a::b::X". 2035f5a7458SNico Weber // \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace 2045f5a7458SNico Weber // will have empty name. 2055f5a7458SNico Weber std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName, 2065f5a7458SNico Weber llvm::StringRef NsName) { 2075f5a7458SNico Weber DeclName = DeclName.ltrim(':'); 2085f5a7458SNico Weber NsName = NsName.ltrim(':'); 20920f0f15aSKazu Hirata if (!DeclName.contains(':')) 210adcd0268SBenjamin Kramer return std::string(DeclName); 2115f5a7458SNico Weber 2125f5a7458SNico Weber auto NsNameSplitted = splitSymbolName(NsName); 2135f5a7458SNico Weber auto DeclNsSplitted = splitSymbolName(DeclName); 2145f5a7458SNico Weber llvm::StringRef UnqualifiedDeclName = DeclNsSplitted.pop_back_val(); 2155f5a7458SNico Weber // If the Decl is in global namespace, there is no need to shorten it. 2165f5a7458SNico Weber if (DeclNsSplitted.empty()) 217adcd0268SBenjamin Kramer return std::string(UnqualifiedDeclName); 2185f5a7458SNico Weber // If NsName is the global namespace, we can simply use the DeclName sans 2195f5a7458SNico Weber // leading "::". 2205f5a7458SNico Weber if (NsNameSplitted.empty()) 221adcd0268SBenjamin Kramer return std::string(DeclName); 2225f5a7458SNico Weber 2235f5a7458SNico Weber if (NsNameSplitted.front() != DeclNsSplitted.front()) { 2245f5a7458SNico Weber // The DeclName must be fully-qualified, but we still need to decide if a 2255f5a7458SNico Weber // leading "::" is necessary. For example, if `NsName` is "a::b::c" and the 2265f5a7458SNico Weber // `DeclName` is "b::X", then the reference must be qualified as "::b::X" 2275f5a7458SNico Weber // to avoid conflict. 2285f5a7458SNico Weber if (llvm::is_contained(NsNameSplitted, DeclNsSplitted.front())) 2295f5a7458SNico Weber return ("::" + DeclName).str(); 230adcd0268SBenjamin Kramer return std::string(DeclName); 2315f5a7458SNico Weber } 2325f5a7458SNico Weber // Since there is already an overlap namespace, we know that `DeclName` can be 2335f5a7458SNico Weber // shortened, so we reduce the longest common prefix. 2345f5a7458SNico Weber auto DeclI = DeclNsSplitted.begin(); 2355f5a7458SNico Weber auto DeclE = DeclNsSplitted.end(); 2365f5a7458SNico Weber auto NsI = NsNameSplitted.begin(); 2375f5a7458SNico Weber auto NsE = NsNameSplitted.end(); 2385f5a7458SNico Weber for (; DeclI != DeclE && NsI != NsE && *DeclI == *NsI; ++DeclI, ++NsI) { 2395f5a7458SNico Weber } 2405f5a7458SNico Weber return (DeclI == DeclE) 2415f5a7458SNico Weber ? UnqualifiedDeclName.str() 2425f5a7458SNico Weber : (llvm::join(DeclI, DeclE, "::") + "::" + UnqualifiedDeclName) 2435f5a7458SNico Weber .str(); 2445f5a7458SNico Weber } 2455f5a7458SNico Weber 2465f5a7458SNico Weber std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) { 2475f5a7458SNico Weber if (Code.back() != '\n') 2485f5a7458SNico Weber Code += "\n"; 2495f5a7458SNico Weber auto NsSplitted = splitSymbolName(NestedNs); 2505f5a7458SNico Weber while (!NsSplitted.empty()) { 2515f5a7458SNico Weber // FIXME: consider code style for comments. 2525f5a7458SNico Weber Code = ("namespace " + NsSplitted.back() + " {\n" + Code + 2535f5a7458SNico Weber "} // namespace " + NsSplitted.back() + "\n") 2545f5a7458SNico Weber .str(); 2555f5a7458SNico Weber NsSplitted.pop_back(); 2565f5a7458SNico Weber } 2575f5a7458SNico Weber return Code; 2585f5a7458SNico Weber } 2595f5a7458SNico Weber 2605f5a7458SNico Weber // Returns true if \p D is a nested DeclContext in \p Context 2615f5a7458SNico Weber bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) { 2625f5a7458SNico Weber while (D) { 2635f5a7458SNico Weber if (D == Context) 2645f5a7458SNico Weber return true; 2655f5a7458SNico Weber D = D->getParent(); 2665f5a7458SNico Weber } 2675f5a7458SNico Weber return false; 2685f5a7458SNico Weber } 2695f5a7458SNico Weber 2705f5a7458SNico Weber // Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx. 2715f5a7458SNico Weber bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D, 2725f5a7458SNico Weber const DeclContext *DeclCtx, SourceLocation Loc) { 2735f5a7458SNico Weber SourceLocation DeclLoc = SM.getSpellingLoc(D->getBeginLoc()); 2745f5a7458SNico Weber Loc = SM.getSpellingLoc(Loc); 2755f5a7458SNico Weber return SM.isBeforeInTranslationUnit(DeclLoc, Loc) && 2765f5a7458SNico Weber (SM.getFileID(DeclLoc) == SM.getFileID(Loc) && 2775f5a7458SNico Weber isNestedDeclContext(DeclCtx, D->getDeclContext())); 2785f5a7458SNico Weber } 2795f5a7458SNico Weber 2805f5a7458SNico Weber // Given a qualified symbol name, returns true if the symbol will be 2815f5a7458SNico Weber // incorrectly qualified without leading "::". For example, a symbol 2825f5a7458SNico Weber // "nx::ny::Foo" in namespace "na::nx::ny" without leading "::"; a symbol 2835f5a7458SNico Weber // "util::X" in namespace "na" can potentially conflict with "na::util" (if this 2845f5a7458SNico Weber // exists). 2855f5a7458SNico Weber bool conflictInNamespace(const ASTContext &AST, llvm::StringRef QualifiedSymbol, 2865f5a7458SNico Weber llvm::StringRef Namespace) { 2875f5a7458SNico Weber auto SymbolSplitted = splitSymbolName(QualifiedSymbol.trim(":")); 2885f5a7458SNico Weber assert(!SymbolSplitted.empty()); 2895f5a7458SNico Weber SymbolSplitted.pop_back(); // We are only interested in namespaces. 2905f5a7458SNico Weber 2915f5a7458SNico Weber if (SymbolSplitted.size() >= 1 && !Namespace.empty()) { 2925f5a7458SNico Weber auto SymbolTopNs = SymbolSplitted.front(); 2935f5a7458SNico Weber auto NsSplitted = splitSymbolName(Namespace.trim(":")); 2945f5a7458SNico Weber assert(!NsSplitted.empty()); 2955f5a7458SNico Weber 2965f5a7458SNico Weber auto LookupDecl = [&AST](const Decl &Scope, 2975f5a7458SNico Weber llvm::StringRef Name) -> const NamedDecl * { 2985f5a7458SNico Weber const auto *DC = llvm::dyn_cast<DeclContext>(&Scope); 2995f5a7458SNico Weber if (!DC) 3005f5a7458SNico Weber return nullptr; 3015f5a7458SNico Weber auto LookupRes = DC->lookup(DeclarationName(&AST.Idents.get(Name))); 3025f5a7458SNico Weber if (LookupRes.empty()) 3035f5a7458SNico Weber return nullptr; 3045f5a7458SNico Weber return LookupRes.front(); 3055f5a7458SNico Weber }; 3065f5a7458SNico Weber // We do not check the outermost namespace since it would not be a 3075f5a7458SNico Weber // conflict if it equals to the symbol's outermost namespace and the 3085f5a7458SNico Weber // symbol name would have been shortened. 3095f5a7458SNico Weber const NamedDecl *Scope = 3105f5a7458SNico Weber LookupDecl(*AST.getTranslationUnitDecl(), NsSplitted.front()); 311246bf08dSKazu Hirata for (const auto &I : llvm::drop_begin(NsSplitted)) { 312246bf08dSKazu Hirata if (I == SymbolTopNs) // Handles "::ny" in "::nx::ny" case. 3135f5a7458SNico Weber return true; 3145f5a7458SNico Weber // Handles "::util" and "::nx::util" conflicts. 3155f5a7458SNico Weber if (Scope) { 3165f5a7458SNico Weber if (LookupDecl(*Scope, SymbolTopNs)) 3175f5a7458SNico Weber return true; 318246bf08dSKazu Hirata Scope = LookupDecl(*Scope, I); 3195f5a7458SNico Weber } 3205f5a7458SNico Weber } 3215f5a7458SNico Weber if (Scope && LookupDecl(*Scope, SymbolTopNs)) 3225f5a7458SNico Weber return true; 3235f5a7458SNico Weber } 3245f5a7458SNico Weber return false; 3255f5a7458SNico Weber } 3265f5a7458SNico Weber 3275f5a7458SNico Weber bool isTemplateParameter(TypeLoc Type) { 3285f5a7458SNico Weber while (!Type.isNull()) { 3295f5a7458SNico Weber if (Type.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm) 3305f5a7458SNico Weber return true; 3315f5a7458SNico Weber Type = Type.getNextTypeLoc(); 3325f5a7458SNico Weber } 3335f5a7458SNico Weber return false; 3345f5a7458SNico Weber } 3355f5a7458SNico Weber 3365f5a7458SNico Weber } // anonymous namespace 3375f5a7458SNico Weber 3385f5a7458SNico Weber ChangeNamespaceTool::ChangeNamespaceTool( 3395f5a7458SNico Weber llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern, 34025ed42f0SEric Christopher llvm::ArrayRef<std::string> AllowedSymbolPatterns, 3415f5a7458SNico Weber std::map<std::string, tooling::Replacements> *FileToReplacements, 3425f5a7458SNico Weber llvm::StringRef FallbackStyle) 3435f5a7458SNico Weber : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements), 3445f5a7458SNico Weber OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')), 3455f5a7458SNico Weber FilePattern(FilePattern), FilePatternRE(FilePattern) { 3465f5a7458SNico Weber FileToReplacements->clear(); 3475f5a7458SNico Weber auto OldNsSplitted = splitSymbolName(OldNamespace); 3485f5a7458SNico Weber auto NewNsSplitted = splitSymbolName(NewNamespace); 3495f5a7458SNico Weber // Calculates `DiffOldNamespace` and `DiffNewNamespace`. 3505f5a7458SNico Weber while (!OldNsSplitted.empty() && !NewNsSplitted.empty() && 3515f5a7458SNico Weber OldNsSplitted.front() == NewNsSplitted.front()) { 3525f5a7458SNico Weber OldNsSplitted.erase(OldNsSplitted.begin()); 3535f5a7458SNico Weber NewNsSplitted.erase(NewNsSplitted.begin()); 3545f5a7458SNico Weber } 3555f5a7458SNico Weber DiffOldNamespace = joinNamespaces(OldNsSplitted); 3565f5a7458SNico Weber DiffNewNamespace = joinNamespaces(NewNsSplitted); 3575f5a7458SNico Weber 35825ed42f0SEric Christopher for (const auto &Pattern : AllowedSymbolPatterns) 35925ed42f0SEric Christopher AllowedSymbolRegexes.emplace_back(Pattern); 3605f5a7458SNico Weber } 3615f5a7458SNico Weber 3625f5a7458SNico Weber void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) { 3635f5a7458SNico Weber std::string FullOldNs = "::" + OldNamespace; 3645f5a7458SNico Weber // Prefix is the outer-most namespace in DiffOldNamespace. For example, if the 3655f5a7458SNico Weber // OldNamespace is "a::b::c" and DiffOldNamespace is "b::c", then Prefix will 3665f5a7458SNico Weber // be "a::b". Declarations in this namespace will not be visible in the new 3675f5a7458SNico Weber // namespace. If DiffOldNamespace is empty, Prefix will be a invalid name "-". 3685f5a7458SNico Weber llvm::SmallVector<llvm::StringRef, 4> DiffOldNsSplitted; 3695f5a7458SNico Weber llvm::StringRef(DiffOldNamespace) 3705f5a7458SNico Weber .split(DiffOldNsSplitted, "::", /*MaxSplit=*/-1, 3715f5a7458SNico Weber /*KeepEmpty=*/false); 3725f5a7458SNico Weber std::string Prefix = "-"; 3735f5a7458SNico Weber if (!DiffOldNsSplitted.empty()) 3745f5a7458SNico Weber Prefix = (StringRef(FullOldNs).drop_back(DiffOldNamespace.size()) + 3755f5a7458SNico Weber DiffOldNsSplitted.front()) 3765f5a7458SNico Weber .str(); 3775f5a7458SNico Weber auto IsInMovedNs = 3785f5a7458SNico Weber allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")), 3795f5a7458SNico Weber isExpansionInFileMatching(FilePattern)); 3805f5a7458SNico Weber auto IsVisibleInNewNs = anyOf( 3815f5a7458SNico Weber IsInMovedNs, unless(hasAncestor(namespaceDecl(hasName(Prefix))))); 3825f5a7458SNico Weber // Match using declarations. 3835f5a7458SNico Weber Finder->addMatcher( 3845f5a7458SNico Weber usingDecl(isExpansionInFileMatching(FilePattern), IsVisibleInNewNs) 3855f5a7458SNico Weber .bind("using"), 3865f5a7458SNico Weber this); 3875f5a7458SNico Weber // Match using namespace declarations. 3885f5a7458SNico Weber Finder->addMatcher(usingDirectiveDecl(isExpansionInFileMatching(FilePattern), 3895f5a7458SNico Weber IsVisibleInNewNs) 3905f5a7458SNico Weber .bind("using_namespace"), 3915f5a7458SNico Weber this); 3925f5a7458SNico Weber // Match namespace alias declarations. 3935f5a7458SNico Weber Finder->addMatcher(namespaceAliasDecl(isExpansionInFileMatching(FilePattern), 3945f5a7458SNico Weber IsVisibleInNewNs) 3955f5a7458SNico Weber .bind("namespace_alias"), 3965f5a7458SNico Weber this); 3975f5a7458SNico Weber 3985f5a7458SNico Weber // Match old namespace blocks. 3995f5a7458SNico Weber Finder->addMatcher( 4005f5a7458SNico Weber namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern)) 4015f5a7458SNico Weber .bind("old_ns"), 4025f5a7458SNico Weber this); 4035f5a7458SNico Weber 4045f5a7458SNico Weber // Match class forward-declarations in the old namespace. 4055f5a7458SNico Weber // Note that forward-declarations in classes are not matched. 4065f5a7458SNico Weber Finder->addMatcher(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())), 4075f5a7458SNico Weber IsInMovedNs, hasParent(namespaceDecl())) 4085f5a7458SNico Weber .bind("class_fwd_decl"), 4095f5a7458SNico Weber this); 4105f5a7458SNico Weber 4115f5a7458SNico Weber // Match template class forward-declarations in the old namespace. 4125f5a7458SNico Weber Finder->addMatcher( 4135f5a7458SNico Weber classTemplateDecl(unless(hasDescendant(cxxRecordDecl(isDefinition()))), 4145f5a7458SNico Weber IsInMovedNs, hasParent(namespaceDecl())) 4155f5a7458SNico Weber .bind("template_class_fwd_decl"), 4165f5a7458SNico Weber this); 4175f5a7458SNico Weber 4185f5a7458SNico Weber // Match references to types that are not defined in the old namespace. 4195f5a7458SNico Weber // Forward-declarations in the old namespace are also matched since they will 4205f5a7458SNico Weber // be moved back to the old namespace. 4215f5a7458SNico Weber auto DeclMatcher = namedDecl( 4225f5a7458SNico Weber hasAncestor(namespaceDecl()), 4235f5a7458SNico Weber unless(anyOf( 4245f5a7458SNico Weber isImplicit(), hasAncestor(namespaceDecl(isAnonymous())), 4255f5a7458SNico Weber hasAncestor(cxxRecordDecl()), 4265f5a7458SNico Weber allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition()))))))); 4275f5a7458SNico Weber 4285f5a7458SNico Weber // Using shadow declarations in classes always refers to base class, which 4295f5a7458SNico Weber // does not need to be qualified since it can be inferred from inheritance. 4305f5a7458SNico Weber // Note that this does not match using alias declarations. 4315f5a7458SNico Weber auto UsingShadowDeclInClass = 4325f5a7458SNico Weber usingDecl(hasAnyUsingShadowDecl(decl()), hasParent(cxxRecordDecl())); 4335f5a7458SNico Weber 4345f5a7458SNico Weber // Match TypeLocs on the declaration. Carefully match only the outermost 4355f5a7458SNico Weber // TypeLoc and template specialization arguments (which are not outermost) 4365f5a7458SNico Weber // that are directly linked to types matching `DeclMatcher`. Nested name 4375f5a7458SNico Weber // specifier locs are handled separately below. 4385f5a7458SNico Weber Finder->addMatcher( 4395f5a7458SNico Weber typeLoc(IsInMovedNs, 4405f5a7458SNico Weber loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))), 4415f5a7458SNico Weber unless(anyOf(hasParent(typeLoc(loc(qualType( 4425f5a7458SNico Weber hasDeclaration(DeclMatcher), 4435f5a7458SNico Weber unless(templateSpecializationType()))))), 4445f5a7458SNico Weber hasParent(nestedNameSpecifierLoc()), 445c8f14827SSam McCall hasAncestor(decl(isImplicit())), 4465f5a7458SNico Weber hasAncestor(UsingShadowDeclInClass), 4475f5a7458SNico Weber hasAncestor(functionDecl(isDefaulted())))), 4485f5a7458SNico Weber hasAncestor(decl().bind("dc"))) 4495f5a7458SNico Weber .bind("type"), 4505f5a7458SNico Weber this); 4515f5a7458SNico Weber 4525f5a7458SNico Weber // Types in `UsingShadowDecl` is not matched by `typeLoc` above, so we need to 4535f5a7458SNico Weber // special case it. 4545f5a7458SNico Weber // Since using declarations inside classes must have the base class in the 4555f5a7458SNico Weber // nested name specifier, we leave it to the nested name specifier matcher. 4565f5a7458SNico Weber Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()), 4575f5a7458SNico Weber unless(UsingShadowDeclInClass)) 4585f5a7458SNico Weber .bind("using_with_shadow"), 4595f5a7458SNico Weber this); 4605f5a7458SNico Weber 4615f5a7458SNico Weber // Handle types in nested name specifier. Specifiers that are in a TypeLoc 4625f5a7458SNico Weber // matched above are not matched, e.g. "A::" in "A::A" is not matched since 4635f5a7458SNico Weber // "A::A" would have already been fixed. 4645f5a7458SNico Weber Finder->addMatcher( 4655f5a7458SNico Weber nestedNameSpecifierLoc( 4665f5a7458SNico Weber hasAncestor(decl(IsInMovedNs).bind("dc")), 4675f5a7458SNico Weber loc(nestedNameSpecifier( 4685f5a7458SNico Weber specifiesType(hasDeclaration(DeclMatcher.bind("from_decl"))))), 469c8f14827SSam McCall unless(anyOf(hasAncestor(decl(isImplicit())), 4705f5a7458SNico Weber hasAncestor(UsingShadowDeclInClass), 4715f5a7458SNico Weber hasAncestor(functionDecl(isDefaulted())), 4725f5a7458SNico Weber hasAncestor(typeLoc(loc(qualType(hasDeclaration( 4735f5a7458SNico Weber decl(equalsBoundNode("from_decl")))))))))) 4745f5a7458SNico Weber .bind("nested_specifier_loc"), 4755f5a7458SNico Weber this); 4765f5a7458SNico Weber 4775f5a7458SNico Weber // Matches base class initializers in constructors. TypeLocs of base class 4785f5a7458SNico Weber // initializers do not need to be fixed. For example, 4795f5a7458SNico Weber // class X : public a::b::Y { 4805f5a7458SNico Weber // public: 4815f5a7458SNico Weber // X() : Y::Y() {} // Y::Y do not need namespace specifier. 4825f5a7458SNico Weber // }; 4835f5a7458SNico Weber Finder->addMatcher( 4845f5a7458SNico Weber cxxCtorInitializer(isBaseInitializer()).bind("base_initializer"), this); 4855f5a7458SNico Weber 4865f5a7458SNico Weber // Handle function. 4875f5a7458SNico Weber // Only handle functions that are defined in a namespace excluding member 4885f5a7458SNico Weber // function, static methods (qualified by nested specifier), and functions 4895f5a7458SNico Weber // defined in the global namespace. 4905f5a7458SNico Weber // Note that the matcher does not exclude calls to out-of-line static method 4915f5a7458SNico Weber // definitions, so we need to exclude them in the callback handler. 4925f5a7458SNico Weber auto FuncMatcher = 4935f5a7458SNico Weber functionDecl(unless(anyOf(cxxMethodDecl(), IsInMovedNs, 4945f5a7458SNico Weber hasAncestor(namespaceDecl(isAnonymous())), 4955f5a7458SNico Weber hasAncestor(cxxRecordDecl()))), 4965f5a7458SNico Weber hasParent(namespaceDecl())); 4975f5a7458SNico Weber Finder->addMatcher(expr(hasAncestor(decl().bind("dc")), IsInMovedNs, 498c8f14827SSam McCall unless(hasAncestor(decl(isImplicit()))), 4995f5a7458SNico Weber anyOf(callExpr(callee(FuncMatcher)).bind("call"), 5005f5a7458SNico Weber declRefExpr(to(FuncMatcher.bind("func_decl"))) 5015f5a7458SNico Weber .bind("func_ref"))), 5025f5a7458SNico Weber this); 5035f5a7458SNico Weber 5045f5a7458SNico Weber auto GlobalVarMatcher = varDecl( 5055f5a7458SNico Weber hasGlobalStorage(), hasParent(namespaceDecl()), 5065f5a7458SNico Weber unless(anyOf(IsInMovedNs, hasAncestor(namespaceDecl(isAnonymous()))))); 5075f5a7458SNico Weber Finder->addMatcher(declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")), 5085f5a7458SNico Weber to(GlobalVarMatcher.bind("var_decl"))) 5095f5a7458SNico Weber .bind("var_ref"), 5105f5a7458SNico Weber this); 5115f5a7458SNico Weber 5125f5a7458SNico Weber // Handle unscoped enum constant. 5135f5a7458SNico Weber auto UnscopedEnumMatcher = enumConstantDecl(hasParent(enumDecl( 5145f5a7458SNico Weber hasParent(namespaceDecl()), 5155f5a7458SNico Weber unless(anyOf(isScoped(), IsInMovedNs, hasAncestor(cxxRecordDecl()), 5165f5a7458SNico Weber hasAncestor(namespaceDecl(isAnonymous()))))))); 5175f5a7458SNico Weber Finder->addMatcher( 5185f5a7458SNico Weber declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")), 5195f5a7458SNico Weber to(UnscopedEnumMatcher.bind("enum_const_decl"))) 5205f5a7458SNico Weber .bind("enum_const_ref"), 5215f5a7458SNico Weber this); 5225f5a7458SNico Weber } 5235f5a7458SNico Weber 5245f5a7458SNico Weber void ChangeNamespaceTool::run( 5255f5a7458SNico Weber const ast_matchers::MatchFinder::MatchResult &Result) { 5265f5a7458SNico Weber if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) { 5275f5a7458SNico Weber UsingDecls.insert(Using); 5285f5a7458SNico Weber } else if (const auto *UsingNamespace = 5295f5a7458SNico Weber Result.Nodes.getNodeAs<UsingDirectiveDecl>( 5305f5a7458SNico Weber "using_namespace")) { 5315f5a7458SNico Weber UsingNamespaceDecls.insert(UsingNamespace); 5325f5a7458SNico Weber } else if (const auto *NamespaceAlias = 5335f5a7458SNico Weber Result.Nodes.getNodeAs<NamespaceAliasDecl>( 5345f5a7458SNico Weber "namespace_alias")) { 5355f5a7458SNico Weber NamespaceAliasDecls.insert(NamespaceAlias); 5365f5a7458SNico Weber } else if (const auto *NsDecl = 5375f5a7458SNico Weber Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) { 5385f5a7458SNico Weber moveOldNamespace(Result, NsDecl); 5395f5a7458SNico Weber } else if (const auto *FwdDecl = 5405f5a7458SNico Weber Result.Nodes.getNodeAs<CXXRecordDecl>("class_fwd_decl")) { 5415f5a7458SNico Weber moveClassForwardDeclaration(Result, cast<NamedDecl>(FwdDecl)); 5425f5a7458SNico Weber } else if (const auto *TemplateFwdDecl = 5435f5a7458SNico Weber Result.Nodes.getNodeAs<ClassTemplateDecl>( 5445f5a7458SNico Weber "template_class_fwd_decl")) { 5455f5a7458SNico Weber moveClassForwardDeclaration(Result, cast<NamedDecl>(TemplateFwdDecl)); 5465f5a7458SNico Weber } else if (const auto *UsingWithShadow = 5475f5a7458SNico Weber Result.Nodes.getNodeAs<UsingDecl>("using_with_shadow")) { 5485f5a7458SNico Weber fixUsingShadowDecl(Result, UsingWithShadow); 5495f5a7458SNico Weber } else if (const auto *Specifier = 5505f5a7458SNico Weber Result.Nodes.getNodeAs<NestedNameSpecifierLoc>( 5515f5a7458SNico Weber "nested_specifier_loc")) { 5525f5a7458SNico Weber SourceLocation Start = Specifier->getBeginLoc(); 5535f5a7458SNico Weber SourceLocation End = endLocationForType(Specifier->getTypeLoc()); 5545f5a7458SNico Weber fixTypeLoc(Result, Start, End, Specifier->getTypeLoc()); 5555f5a7458SNico Weber } else if (const auto *BaseInitializer = 5565f5a7458SNico Weber Result.Nodes.getNodeAs<CXXCtorInitializer>( 5575f5a7458SNico Weber "base_initializer")) { 5585f5a7458SNico Weber BaseCtorInitializerTypeLocs.push_back( 5595f5a7458SNico Weber BaseInitializer->getTypeSourceInfo()->getTypeLoc()); 5605f5a7458SNico Weber } else if (const auto *TLoc = Result.Nodes.getNodeAs<TypeLoc>("type")) { 5615f5a7458SNico Weber // This avoids fixing types with record types as qualifier, which is not 5625f5a7458SNico Weber // filtered by matchers in some cases, e.g. the type is templated. We should 5635f5a7458SNico Weber // handle the record type qualifier instead. 5645f5a7458SNico Weber TypeLoc Loc = *TLoc; 5655f5a7458SNico Weber while (Loc.getTypeLocClass() == TypeLoc::Qualified) 5665f5a7458SNico Weber Loc = Loc.getNextTypeLoc(); 5675f5a7458SNico Weber if (Loc.getTypeLocClass() == TypeLoc::Elaborated) { 5685f5a7458SNico Weber NestedNameSpecifierLoc NestedNameSpecifier = 5695f5a7458SNico Weber Loc.castAs<ElaboratedTypeLoc>().getQualifierLoc(); 57015f3cd6bSMatheus Izvekov // FIXME: avoid changing injected class names. 57115f3cd6bSMatheus Izvekov if (auto *NNS = NestedNameSpecifier.getNestedNameSpecifier()) { 57215f3cd6bSMatheus Izvekov const Type *SpecifierType = NNS->getAsType(); 5735f5a7458SNico Weber if (SpecifierType && SpecifierType->isRecordType()) 5745f5a7458SNico Weber return; 5755f5a7458SNico Weber } 57615f3cd6bSMatheus Izvekov } 5775f5a7458SNico Weber fixTypeLoc(Result, startLocationForType(Loc), endLocationForType(Loc), Loc); 5785f5a7458SNico Weber } else if (const auto *VarRef = 5795f5a7458SNico Weber Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) { 5805f5a7458SNico Weber const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl"); 5815f5a7458SNico Weber assert(Var); 5825f5a7458SNico Weber if (Var->getCanonicalDecl()->isStaticDataMember()) 5835f5a7458SNico Weber return; 5845f5a7458SNico Weber const auto *Context = Result.Nodes.getNodeAs<Decl>("dc"); 5855f5a7458SNico Weber assert(Context && "Empty decl context."); 5865f5a7458SNico Weber fixDeclRefExpr(Result, Context->getDeclContext(), 5875f5a7458SNico Weber llvm::cast<NamedDecl>(Var), VarRef); 5885f5a7458SNico Weber } else if (const auto *EnumConstRef = 5895f5a7458SNico Weber Result.Nodes.getNodeAs<DeclRefExpr>("enum_const_ref")) { 5905f5a7458SNico Weber // Do not rename the reference if it is already scoped by the EnumDecl name. 5915f5a7458SNico Weber if (EnumConstRef->hasQualifier() && 5925f5a7458SNico Weber EnumConstRef->getQualifier()->getKind() == 5935f5a7458SNico Weber NestedNameSpecifier::SpecifierKind::TypeSpec && 5945f5a7458SNico Weber EnumConstRef->getQualifier()->getAsType()->isEnumeralType()) 5955f5a7458SNico Weber return; 5965f5a7458SNico Weber const auto *EnumConstDecl = 5975f5a7458SNico Weber Result.Nodes.getNodeAs<EnumConstantDecl>("enum_const_decl"); 5985f5a7458SNico Weber assert(EnumConstDecl); 5995f5a7458SNico Weber const auto *Context = Result.Nodes.getNodeAs<Decl>("dc"); 6005f5a7458SNico Weber assert(Context && "Empty decl context."); 6015f5a7458SNico Weber // FIXME: this would qualify "ns::VALUE" as "ns::EnumValue::VALUE". Fix it 6025f5a7458SNico Weber // if it turns out to be an issue. 6035f5a7458SNico Weber fixDeclRefExpr(Result, Context->getDeclContext(), 6045f5a7458SNico Weber llvm::cast<NamedDecl>(EnumConstDecl), EnumConstRef); 6055f5a7458SNico Weber } else if (const auto *FuncRef = 6065f5a7458SNico Weber Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) { 6075f5a7458SNico Weber // If this reference has been processed as a function call, we do not 6085f5a7458SNico Weber // process it again. 609*d2a96d17SKazu Hirata if (!ProcessedFuncRefs.insert(FuncRef).second) 6105f5a7458SNico Weber return; 6115f5a7458SNico Weber const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func_decl"); 6125f5a7458SNico Weber assert(Func); 6135f5a7458SNico Weber const auto *Context = Result.Nodes.getNodeAs<Decl>("dc"); 6145f5a7458SNico Weber assert(Context && "Empty decl context."); 6155f5a7458SNico Weber fixDeclRefExpr(Result, Context->getDeclContext(), 6165f5a7458SNico Weber llvm::cast<NamedDecl>(Func), FuncRef); 6175f5a7458SNico Weber } else { 6185f5a7458SNico Weber const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call"); 6195f5a7458SNico Weber assert(Call != nullptr && "Expecting callback for CallExpr."); 6205f5a7458SNico Weber const auto *CalleeFuncRef = 6215f5a7458SNico Weber llvm::cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit()); 6225f5a7458SNico Weber ProcessedFuncRefs.insert(CalleeFuncRef); 6235f5a7458SNico Weber const FunctionDecl *Func = Call->getDirectCallee(); 6245f5a7458SNico Weber assert(Func != nullptr); 6255f5a7458SNico Weber // FIXME: ignore overloaded operators. This would miss cases where operators 6265f5a7458SNico Weber // are called by qualified names (i.e. "ns::operator <"). Ignore such 6275f5a7458SNico Weber // cases for now. 6285f5a7458SNico Weber if (Func->isOverloadedOperator()) 6295f5a7458SNico Weber return; 6305f5a7458SNico Weber // Ignore out-of-line static methods since they will be handled by nested 6315f5a7458SNico Weber // name specifiers. 6322fd11e0bSThorsten Schütt if (Func->getCanonicalDecl()->getStorageClass() == 6332fd11e0bSThorsten Schütt StorageClass::SC_Static && 6345f5a7458SNico Weber Func->isOutOfLine()) 6355f5a7458SNico Weber return; 6365f5a7458SNico Weber const auto *Context = Result.Nodes.getNodeAs<Decl>("dc"); 6375f5a7458SNico Weber assert(Context && "Empty decl context."); 6385f5a7458SNico Weber SourceRange CalleeRange = Call->getCallee()->getSourceRange(); 6395f5a7458SNico Weber replaceQualifiedSymbolInDeclContext( 6405f5a7458SNico Weber Result, Context->getDeclContext(), CalleeRange.getBegin(), 6415f5a7458SNico Weber CalleeRange.getEnd(), llvm::cast<NamedDecl>(Func)); 6425f5a7458SNico Weber } 6435f5a7458SNico Weber } 6445f5a7458SNico Weber 6455f5a7458SNico Weber static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl, 6465f5a7458SNico Weber const SourceManager &SM, 6475f5a7458SNico Weber const LangOptions &LangOpts) { 6485f5a7458SNico Weber std::unique_ptr<Lexer> Lex = 6495f5a7458SNico Weber getLexerStartingFromLoc(NsDecl->getBeginLoc(), SM, LangOpts); 6505f5a7458SNico Weber assert(Lex.get() && 6515f5a7458SNico Weber "Failed to create lexer from the beginning of namespace."); 6525f5a7458SNico Weber if (!Lex.get()) 6535f5a7458SNico Weber return SourceLocation(); 6545f5a7458SNico Weber Token Tok; 6555f5a7458SNico Weber while (!Lex->LexFromRawLexer(Tok) && Tok.isNot(tok::TokenKind::l_brace)) { 6565f5a7458SNico Weber } 6575f5a7458SNico Weber return Tok.isNot(tok::TokenKind::l_brace) 6585f5a7458SNico Weber ? SourceLocation() 6595f5a7458SNico Weber : Tok.getEndLoc().getLocWithOffset(1); 6605f5a7458SNico Weber } 6615f5a7458SNico Weber 6625f5a7458SNico Weber // Stores information about a moved namespace in `MoveNamespaces` and leaves 6635f5a7458SNico Weber // the actual movement to `onEndOfTranslationUnit()`. 6645f5a7458SNico Weber void ChangeNamespaceTool::moveOldNamespace( 6655f5a7458SNico Weber const ast_matchers::MatchFinder::MatchResult &Result, 6665f5a7458SNico Weber const NamespaceDecl *NsDecl) { 6675f5a7458SNico Weber // If the namespace is empty, do nothing. 6685f5a7458SNico Weber if (Decl::castToDeclContext(NsDecl)->decls_empty()) 6695f5a7458SNico Weber return; 6705f5a7458SNico Weber 6715f5a7458SNico Weber const SourceManager &SM = *Result.SourceManager; 6725f5a7458SNico Weber // Get the range of the code in the old namespace. 6735f5a7458SNico Weber SourceLocation Start = 6745f5a7458SNico Weber getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()); 6755f5a7458SNico Weber assert(Start.isValid() && "Can't find l_brace for namespace."); 6765f5a7458SNico Weber MoveNamespace MoveNs; 6775f5a7458SNico Weber MoveNs.Offset = SM.getFileOffset(Start); 6785f5a7458SNico Weber // The range of the moved namespace is from the location just past the left 6795f5a7458SNico Weber // brace to the location right before the right brace. 6805f5a7458SNico Weber MoveNs.Length = SM.getFileOffset(NsDecl->getRBraceLoc()) - MoveNs.Offset; 6815f5a7458SNico Weber 6825f5a7458SNico Weber // Insert the new namespace after `DiffOldNamespace`. For example, if 6835f5a7458SNico Weber // `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then 6845f5a7458SNico Weber // "x::y" will be inserted inside the existing namespace "a" and after "a::b". 6855f5a7458SNico Weber // `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b" 6865f5a7458SNico Weber // in the above example. 6875f5a7458SNico Weber // If there is no outer namespace (i.e. DiffOldNamespace is empty), the new 6885f5a7458SNico Weber // namespace will be a nested namespace in the old namespace. 6895f5a7458SNico Weber const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace); 6905f5a7458SNico Weber SourceLocation InsertionLoc = Start; 6915f5a7458SNico Weber if (OuterNs) { 6925f5a7458SNico Weber SourceLocation LocAfterNs = getStartOfNextLine( 6935f5a7458SNico Weber OuterNs->getRBraceLoc(), SM, Result.Context->getLangOpts()); 6945f5a7458SNico Weber assert(LocAfterNs.isValid() && 6955f5a7458SNico Weber "Failed to get location after DiffOldNamespace"); 6965f5a7458SNico Weber InsertionLoc = LocAfterNs; 6975f5a7458SNico Weber } 6985f5a7458SNico Weber MoveNs.InsertionOffset = SM.getFileOffset(SM.getSpellingLoc(InsertionLoc)); 6995f5a7458SNico Weber MoveNs.FID = SM.getFileID(Start); 7005f5a7458SNico Weber MoveNs.SourceMgr = Result.SourceManager; 701adcd0268SBenjamin Kramer MoveNamespaces[std::string(SM.getFilename(Start))].push_back(MoveNs); 7025f5a7458SNico Weber } 7035f5a7458SNico Weber 7045f5a7458SNico Weber // Removes a class forward declaration from the code in the moved namespace and 7055f5a7458SNico Weber // creates an `InsertForwardDeclaration` to insert the forward declaration back 7065f5a7458SNico Weber // into the old namespace after moving code from the old namespace to the new 7075f5a7458SNico Weber // namespace. 7085f5a7458SNico Weber // For example, changing "a" to "x": 7095f5a7458SNico Weber // Old code: 7105f5a7458SNico Weber // namespace a { 7115f5a7458SNico Weber // class FWD; 7125f5a7458SNico Weber // class A { FWD *fwd; } 7135f5a7458SNico Weber // } // a 7145f5a7458SNico Weber // New code: 7155f5a7458SNico Weber // namespace a { 7165f5a7458SNico Weber // class FWD; 7175f5a7458SNico Weber // } // a 7185f5a7458SNico Weber // namespace x { 7195f5a7458SNico Weber // class A { a::FWD *fwd; } 7205f5a7458SNico Weber // } // x 7215f5a7458SNico Weber void ChangeNamespaceTool::moveClassForwardDeclaration( 7225f5a7458SNico Weber const ast_matchers::MatchFinder::MatchResult &Result, 7235f5a7458SNico Weber const NamedDecl *FwdDecl) { 7245f5a7458SNico Weber SourceLocation Start = FwdDecl->getBeginLoc(); 7255f5a7458SNico Weber SourceLocation End = FwdDecl->getEndLoc(); 7265f5a7458SNico Weber const SourceManager &SM = *Result.SourceManager; 7275f5a7458SNico Weber SourceLocation AfterSemi = Lexer::findLocationAfterToken( 7285f5a7458SNico Weber End, tok::semi, SM, Result.Context->getLangOpts(), 7295f5a7458SNico Weber /*SkipTrailingWhitespaceAndNewLine=*/true); 7305f5a7458SNico Weber if (AfterSemi.isValid()) 7315f5a7458SNico Weber End = AfterSemi.getLocWithOffset(-1); 7325f5a7458SNico Weber // Delete the forward declaration from the code to be moved. 7335f5a7458SNico Weber addReplacementOrDie(Start, End, "", SM, &FileToReplacements); 7345f5a7458SNico Weber llvm::StringRef Code = Lexer::getSourceText( 7355f5a7458SNico Weber CharSourceRange::getTokenRange(SM.getSpellingLoc(Start), 7365f5a7458SNico Weber SM.getSpellingLoc(End)), 7375f5a7458SNico Weber SM, Result.Context->getLangOpts()); 7385f5a7458SNico Weber // Insert the forward declaration back into the old namespace after moving the 7395f5a7458SNico Weber // code from old namespace to new namespace. 7405f5a7458SNico Weber // Insertion information is stored in `InsertFwdDecls` and actual 7415f5a7458SNico Weber // insertion will be performed in `onEndOfTranslationUnit`. 7425f5a7458SNico Weber // Get the (old) namespace that contains the forward declaration. 7435f5a7458SNico Weber const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl"); 7445f5a7458SNico Weber // The namespace contains the forward declaration, so it must not be empty. 7455f5a7458SNico Weber assert(!NsDecl->decls_empty()); 7465f5a7458SNico Weber const auto Insertion = createInsertion( 7475f5a7458SNico Weber getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()), 7485f5a7458SNico Weber Code, SM); 7495f5a7458SNico Weber InsertForwardDeclaration InsertFwd; 7505f5a7458SNico Weber InsertFwd.InsertionOffset = Insertion.getOffset(); 7515f5a7458SNico Weber InsertFwd.ForwardDeclText = Insertion.getReplacementText().str(); 752adcd0268SBenjamin Kramer InsertFwdDecls[std::string(Insertion.getFilePath())].push_back(InsertFwd); 7535f5a7458SNico Weber } 7545f5a7458SNico Weber 7555f5a7458SNico Weber // Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p 7565f5a7458SNico Weber // FromDecl with the shortest qualified name possible when the reference is in 7575f5a7458SNico Weber // `NewNamespace`. 7585f5a7458SNico Weber void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext( 7595f5a7458SNico Weber const ast_matchers::MatchFinder::MatchResult &Result, 7605f5a7458SNico Weber const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End, 7615f5a7458SNico Weber const NamedDecl *FromDecl) { 7625f5a7458SNico Weber const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext(); 7635f5a7458SNico Weber if (llvm::isa<TranslationUnitDecl>(NsDeclContext)) { 7645f5a7458SNico Weber // This should not happen in usual unless the TypeLoc is in function type 7655f5a7458SNico Weber // parameters, e.g `std::function<void(T)>`. In this case, DeclContext of 7665f5a7458SNico Weber // `T` will be the translation unit. We simply use fully-qualified name 7675f5a7458SNico Weber // here. 7685f5a7458SNico Weber // Note that `FromDecl` must not be defined in the old namespace (according 7695f5a7458SNico Weber // to `DeclMatcher`), so its fully-qualified name will not change after 7705f5a7458SNico Weber // changing the namespace. 7715f5a7458SNico Weber addReplacementOrDie(Start, End, FromDecl->getQualifiedNameAsString(), 7725f5a7458SNico Weber *Result.SourceManager, &FileToReplacements); 7735f5a7458SNico Weber return; 7745f5a7458SNico Weber } 7755f5a7458SNico Weber const auto *NsDecl = llvm::cast<NamespaceDecl>(NsDeclContext); 7765f5a7458SNico Weber // Calculate the name of the `NsDecl` after it is moved to new namespace. 7775f5a7458SNico Weber std::string OldNs = NsDecl->getQualifiedNameAsString(); 7785f5a7458SNico Weber llvm::StringRef Postfix = OldNs; 7795f5a7458SNico Weber bool Consumed = Postfix.consume_front(OldNamespace); 7805f5a7458SNico Weber assert(Consumed && "Expect OldNS to start with OldNamespace."); 7815f5a7458SNico Weber (void)Consumed; 7825f5a7458SNico Weber const std::string NewNs = (NewNamespace + Postfix).str(); 7835f5a7458SNico Weber 7845f5a7458SNico Weber llvm::StringRef NestedName = Lexer::getSourceText( 7855f5a7458SNico Weber CharSourceRange::getTokenRange( 7865f5a7458SNico Weber Result.SourceManager->getSpellingLoc(Start), 7875f5a7458SNico Weber Result.SourceManager->getSpellingLoc(End)), 7885f5a7458SNico Weber *Result.SourceManager, Result.Context->getLangOpts()); 7895f5a7458SNico Weber std::string FromDeclName = FromDecl->getQualifiedNameAsString(); 79025ed42f0SEric Christopher for (llvm::Regex &RE : AllowedSymbolRegexes) 7915f5a7458SNico Weber if (RE.match(FromDeclName)) 7925f5a7458SNico Weber return; 7935f5a7458SNico Weber std::string ReplaceName = 7945f5a7458SNico Weber getShortestQualifiedNameInNamespace(FromDeclName, NewNs); 7955f5a7458SNico Weber // Checks if there is any using namespace declarations that can shorten the 7965f5a7458SNico Weber // qualified name. 7975f5a7458SNico Weber for (const auto *UsingNamespace : UsingNamespaceDecls) { 7985f5a7458SNico Weber if (!isDeclVisibleAtLocation(*Result.SourceManager, UsingNamespace, DeclCtx, 7995f5a7458SNico Weber Start)) 8005f5a7458SNico Weber continue; 8015f5a7458SNico Weber StringRef FromDeclNameRef = FromDeclName; 8025f5a7458SNico Weber if (FromDeclNameRef.consume_front(UsingNamespace->getNominatedNamespace() 8035f5a7458SNico Weber ->getQualifiedNameAsString())) { 8045f5a7458SNico Weber FromDeclNameRef = FromDeclNameRef.drop_front(2); 8055f5a7458SNico Weber if (FromDeclNameRef.size() < ReplaceName.size()) 806adcd0268SBenjamin Kramer ReplaceName = std::string(FromDeclNameRef); 8075f5a7458SNico Weber } 8085f5a7458SNico Weber } 8095f5a7458SNico Weber // Checks if there is any namespace alias declarations that can shorten the 8105f5a7458SNico Weber // qualified name. 8115f5a7458SNico Weber for (const auto *NamespaceAlias : NamespaceAliasDecls) { 8125f5a7458SNico Weber if (!isDeclVisibleAtLocation(*Result.SourceManager, NamespaceAlias, DeclCtx, 8135f5a7458SNico Weber Start)) 8145f5a7458SNico Weber continue; 8155f5a7458SNico Weber StringRef FromDeclNameRef = FromDeclName; 8165f5a7458SNico Weber if (FromDeclNameRef.consume_front( 8175f5a7458SNico Weber NamespaceAlias->getNamespace()->getQualifiedNameAsString() + 8185f5a7458SNico Weber "::")) { 8195f5a7458SNico Weber std::string AliasName = NamespaceAlias->getNameAsString(); 8205f5a7458SNico Weber std::string AliasQualifiedName = 8215f5a7458SNico Weber NamespaceAlias->getQualifiedNameAsString(); 822dd5571d5SKazuaki Ishizaki // We only consider namespace aliases define in the global namespace or 8235f5a7458SNico Weber // in namespaces that are directly visible from the reference, i.e. 8245f5a7458SNico Weber // ancestor of the `OldNs`. Note that declarations in ancestor namespaces 8255f5a7458SNico Weber // but not visible in the new namespace is filtered out by 8265f5a7458SNico Weber // "IsVisibleInNewNs" matcher. 8275f5a7458SNico Weber if (AliasQualifiedName != AliasName) { 8285f5a7458SNico Weber // The alias is defined in some namespace. 829732bccb8SKazu Hirata assert(StringRef(AliasQualifiedName).ends_with("::" + AliasName)); 8305f5a7458SNico Weber llvm::StringRef AliasNs = 8315f5a7458SNico Weber StringRef(AliasQualifiedName).drop_back(AliasName.size() + 2); 832732bccb8SKazu Hirata if (!llvm::StringRef(OldNs).starts_with(AliasNs)) 8335f5a7458SNico Weber continue; 8345f5a7458SNico Weber } 8355f5a7458SNico Weber std::string NameWithAliasNamespace = 8365f5a7458SNico Weber (AliasName + "::" + FromDeclNameRef).str(); 8375f5a7458SNico Weber if (NameWithAliasNamespace.size() < ReplaceName.size()) 8385f5a7458SNico Weber ReplaceName = NameWithAliasNamespace; 8395f5a7458SNico Weber } 8405f5a7458SNico Weber } 8415f5a7458SNico Weber // Checks if there is any using shadow declarations that can shorten the 8425f5a7458SNico Weber // qualified name. 8435f5a7458SNico Weber bool Matched = false; 8445f5a7458SNico Weber for (const UsingDecl *Using : UsingDecls) { 8455f5a7458SNico Weber if (Matched) 8465f5a7458SNico Weber break; 8475f5a7458SNico Weber if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) { 8485f5a7458SNico Weber for (const auto *UsingShadow : Using->shadows()) { 8495f5a7458SNico Weber const auto *TargetDecl = UsingShadow->getTargetDecl(); 8505f5a7458SNico Weber if (TargetDecl->getQualifiedNameAsString() == 8515f5a7458SNico Weber FromDecl->getQualifiedNameAsString()) { 8525f5a7458SNico Weber ReplaceName = FromDecl->getNameAsString(); 8535f5a7458SNico Weber Matched = true; 8545f5a7458SNico Weber break; 8555f5a7458SNico Weber } 8565f5a7458SNico Weber } 8575f5a7458SNico Weber } 8585f5a7458SNico Weber } 8595f5a7458SNico Weber bool Conflict = conflictInNamespace(DeclCtx->getParentASTContext(), 8605f5a7458SNico Weber ReplaceName, NewNamespace); 8615f5a7458SNico Weber // If the new nested name in the new namespace is the same as it was in the 8625f5a7458SNico Weber // old namespace, we don't create replacement unless there can be ambiguity. 8635f5a7458SNico Weber if ((NestedName == ReplaceName && !Conflict) || 864732bccb8SKazu Hirata (NestedName.starts_with("::") && NestedName.drop_front(2) == ReplaceName)) 8655f5a7458SNico Weber return; 8665f5a7458SNico Weber // If the reference need to be fully-qualified, add a leading "::" unless 8675f5a7458SNico Weber // NewNamespace is the global namespace. 8685f5a7458SNico Weber if (ReplaceName == FromDeclName && !NewNamespace.empty() && Conflict) 8695f5a7458SNico Weber ReplaceName = "::" + ReplaceName; 8705f5a7458SNico Weber addReplacementOrDie(Start, End, ReplaceName, *Result.SourceManager, 8715f5a7458SNico Weber &FileToReplacements); 8725f5a7458SNico Weber } 8735f5a7458SNico Weber 8745f5a7458SNico Weber // Replace the [Start, End] of `Type` with the shortest qualified name when the 8755f5a7458SNico Weber // `Type` is in `NewNamespace`. 8765f5a7458SNico Weber void ChangeNamespaceTool::fixTypeLoc( 8775f5a7458SNico Weber const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start, 8785f5a7458SNico Weber SourceLocation End, TypeLoc Type) { 8795f5a7458SNico Weber // FIXME: do not rename template parameter. 8805f5a7458SNico Weber if (Start.isInvalid() || End.isInvalid()) 8815f5a7458SNico Weber return; 8825f5a7458SNico Weber // Types of CXXCtorInitializers do not need to be fixed. 8835f5a7458SNico Weber if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type)) 8845f5a7458SNico Weber return; 8855f5a7458SNico Weber if (isTemplateParameter(Type)) 8865f5a7458SNico Weber return; 8875f5a7458SNico Weber // The declaration which this TypeLoc refers to. 8885f5a7458SNico Weber const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl"); 8895f5a7458SNico Weber // `hasDeclaration` gives underlying declaration, but if the type is 8905f5a7458SNico Weber // a typedef type, we need to use the typedef type instead. 8915f5a7458SNico Weber auto IsInMovedNs = [&](const NamedDecl *D) { 8925f5a7458SNico Weber if (!llvm::StringRef(D->getQualifiedNameAsString()) 893732bccb8SKazu Hirata .starts_with(OldNamespace + "::")) 8945f5a7458SNico Weber return false; 8955f5a7458SNico Weber auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getBeginLoc()); 8965f5a7458SNico Weber if (ExpansionLoc.isInvalid()) 8975f5a7458SNico Weber return false; 8985f5a7458SNico Weber llvm::StringRef Filename = Result.SourceManager->getFilename(ExpansionLoc); 8995f5a7458SNico Weber return FilePatternRE.match(Filename); 9005f5a7458SNico Weber }; 9015f5a7458SNico Weber // Make `FromDecl` the immediate declaration that `Type` refers to, i.e. if 9025f5a7458SNico Weber // `Type` is an alias type, we make `FromDecl` the type alias declaration. 9035f5a7458SNico Weber // Also, don't fix the \p Type if it refers to a type alias decl in the moved 9045f5a7458SNico Weber // namespace since the alias decl will be moved along with the type reference. 9055f5a7458SNico Weber if (auto *Typedef = Type.getType()->getAs<TypedefType>()) { 9065f5a7458SNico Weber FromDecl = Typedef->getDecl(); 9075f5a7458SNico Weber if (IsInMovedNs(FromDecl)) 9085f5a7458SNico Weber return; 9095f5a7458SNico Weber } else if (auto *TemplateType = 9105f5a7458SNico Weber Type.getType()->getAs<TemplateSpecializationType>()) { 9115f5a7458SNico Weber if (TemplateType->isTypeAlias()) { 9125f5a7458SNico Weber FromDecl = TemplateType->getTemplateName().getAsTemplateDecl(); 9135f5a7458SNico Weber if (IsInMovedNs(FromDecl)) 9145f5a7458SNico Weber return; 9155f5a7458SNico Weber } 9165f5a7458SNico Weber } 9175f5a7458SNico Weber const auto *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc"); 9185f5a7458SNico Weber assert(DeclCtx && "Empty decl context."); 9195f5a7458SNico Weber replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start, 9205f5a7458SNico Weber End, FromDecl); 9215f5a7458SNico Weber } 9225f5a7458SNico Weber 9235f5a7458SNico Weber void ChangeNamespaceTool::fixUsingShadowDecl( 9245f5a7458SNico Weber const ast_matchers::MatchFinder::MatchResult &Result, 9255f5a7458SNico Weber const UsingDecl *UsingDeclaration) { 9265f5a7458SNico Weber SourceLocation Start = UsingDeclaration->getBeginLoc(); 9275f5a7458SNico Weber SourceLocation End = UsingDeclaration->getEndLoc(); 9285f5a7458SNico Weber if (Start.isInvalid() || End.isInvalid()) 9295f5a7458SNico Weber return; 9305f5a7458SNico Weber 9315f5a7458SNico Weber assert(UsingDeclaration->shadow_size() > 0); 9325f5a7458SNico Weber // FIXME: it might not be always accurate to use the first using-decl. 9335f5a7458SNico Weber const NamedDecl *TargetDecl = 9345f5a7458SNico Weber UsingDeclaration->shadow_begin()->getTargetDecl(); 9355f5a7458SNico Weber std::string TargetDeclName = TargetDecl->getQualifiedNameAsString(); 9365f5a7458SNico Weber // FIXME: check if target_decl_name is in moved ns, which doesn't make much 9375f5a7458SNico Weber // sense. If this happens, we need to use name with the new namespace. 9385f5a7458SNico Weber // Use fully qualified name in UsingDecl for now. 9395f5a7458SNico Weber addReplacementOrDie(Start, End, "using ::" + TargetDeclName, 9405f5a7458SNico Weber *Result.SourceManager, &FileToReplacements); 9415f5a7458SNico Weber } 9425f5a7458SNico Weber 9435f5a7458SNico Weber void ChangeNamespaceTool::fixDeclRefExpr( 9445f5a7458SNico Weber const ast_matchers::MatchFinder::MatchResult &Result, 9455f5a7458SNico Weber const DeclContext *UseContext, const NamedDecl *From, 9465f5a7458SNico Weber const DeclRefExpr *Ref) { 9475f5a7458SNico Weber SourceRange RefRange = Ref->getSourceRange(); 9485f5a7458SNico Weber replaceQualifiedSymbolInDeclContext(Result, UseContext, RefRange.getBegin(), 9495f5a7458SNico Weber RefRange.getEnd(), From); 9505f5a7458SNico Weber } 9515f5a7458SNico Weber 9525f5a7458SNico Weber void ChangeNamespaceTool::onEndOfTranslationUnit() { 9535f5a7458SNico Weber // Move namespace blocks and insert forward declaration to old namespace. 9545f5a7458SNico Weber for (const auto &FileAndNsMoves : MoveNamespaces) { 9555f5a7458SNico Weber auto &NsMoves = FileAndNsMoves.second; 9565f5a7458SNico Weber if (NsMoves.empty()) 9575f5a7458SNico Weber continue; 9585f5a7458SNico Weber const std::string &FilePath = FileAndNsMoves.first; 9595f5a7458SNico Weber auto &Replaces = FileToReplacements[FilePath]; 9605f5a7458SNico Weber auto &SM = *NsMoves.begin()->SourceMgr; 9615f5a7458SNico Weber llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID); 9625f5a7458SNico Weber auto ChangedCode = tooling::applyAllReplacements(Code, Replaces); 9635f5a7458SNico Weber if (!ChangedCode) { 9645f5a7458SNico Weber llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n"; 9655f5a7458SNico Weber continue; 9665f5a7458SNico Weber } 9675f5a7458SNico Weber // Replacements on the changed code for moving namespaces and inserting 9685f5a7458SNico Weber // forward declarations to old namespaces. 9695f5a7458SNico Weber tooling::Replacements NewReplacements; 9705f5a7458SNico Weber // Cut the changed code from the old namespace and paste the code in the new 9715f5a7458SNico Weber // namespace. 9725f5a7458SNico Weber for (const auto &NsMove : NsMoves) { 9735f5a7458SNico Weber // Calculate the range of the old namespace block in the changed 9745f5a7458SNico Weber // code. 9755f5a7458SNico Weber const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset); 9765f5a7458SNico Weber const unsigned NewLength = 9775f5a7458SNico Weber Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) - 9785f5a7458SNico Weber NewOffset; 9795f5a7458SNico Weber tooling::Replacement Deletion(FilePath, NewOffset, NewLength, ""); 9805f5a7458SNico Weber std::string MovedCode = ChangedCode->substr(NewOffset, NewLength); 9815f5a7458SNico Weber std::string MovedCodeWrappedInNewNs = 9825f5a7458SNico Weber wrapCodeInNamespace(DiffNewNamespace, MovedCode); 9835f5a7458SNico Weber // Calculate the new offset at which the code will be inserted in the 9845f5a7458SNico Weber // changed code. 9855f5a7458SNico Weber unsigned NewInsertionOffset = 9865f5a7458SNico Weber Replaces.getShiftedCodePosition(NsMove.InsertionOffset); 9875f5a7458SNico Weber tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0, 9885f5a7458SNico Weber MovedCodeWrappedInNewNs); 9895f5a7458SNico Weber addOrMergeReplacement(Deletion, &NewReplacements); 9905f5a7458SNico Weber addOrMergeReplacement(Insertion, &NewReplacements); 9915f5a7458SNico Weber } 9925f5a7458SNico Weber // After moving namespaces, insert forward declarations back to old 9935f5a7458SNico Weber // namespaces. 9945f5a7458SNico Weber const auto &FwdDeclInsertions = InsertFwdDecls[FilePath]; 9955f5a7458SNico Weber for (const auto &FwdDeclInsertion : FwdDeclInsertions) { 9965f5a7458SNico Weber unsigned NewInsertionOffset = 9975f5a7458SNico Weber Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset); 9985f5a7458SNico Weber tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0, 9995f5a7458SNico Weber FwdDeclInsertion.ForwardDeclText); 10005f5a7458SNico Weber addOrMergeReplacement(Insertion, &NewReplacements); 10015f5a7458SNico Weber } 10025f5a7458SNico Weber // Add replacements referring to the changed code to existing replacements, 10035f5a7458SNico Weber // which refers to the original code. 10045f5a7458SNico Weber Replaces = Replaces.merge(NewReplacements); 10055f5a7458SNico Weber auto Style = 10065f5a7458SNico Weber format::getStyle(format::DefaultFormatStyle, FilePath, FallbackStyle); 10075f5a7458SNico Weber if (!Style) { 10085f5a7458SNico Weber llvm::errs() << llvm::toString(Style.takeError()) << "\n"; 10095f5a7458SNico Weber continue; 10105f5a7458SNico Weber } 10115f5a7458SNico Weber // Clean up old namespaces if there is nothing in it after moving. 10125f5a7458SNico Weber auto CleanReplacements = 10135f5a7458SNico Weber format::cleanupAroundReplacements(Code, Replaces, *Style); 10145f5a7458SNico Weber if (!CleanReplacements) { 10155f5a7458SNico Weber llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n"; 10165f5a7458SNico Weber continue; 10175f5a7458SNico Weber } 10185f5a7458SNico Weber FileToReplacements[FilePath] = *CleanReplacements; 10195f5a7458SNico Weber } 10205f5a7458SNico Weber 10215f5a7458SNico Weber // Make sure we don't generate replacements for files that do not match 10225f5a7458SNico Weber // FilePattern. 10235f5a7458SNico Weber for (auto &Entry : FileToReplacements) 10245f5a7458SNico Weber if (!FilePatternRE.match(Entry.first)) 10255f5a7458SNico Weber Entry.second.clear(); 10265f5a7458SNico Weber } 10275f5a7458SNico Weber 10285f5a7458SNico Weber } // namespace change_namespace 10295f5a7458SNico Weber } // namespace clang 1030