Index: clang-tools-extra/trunk/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/CMakeLists.txt +++ clang-tools-extra/trunk/CMakeLists.txt @@ -9,7 +9,7 @@ add_subdirectory(clang-tidy) add_subdirectory(clang-tidy-vs) -add_subdirectory(change-namespace) +add_subdirectory(clang-change-namespace) add_subdirectory(clang-doc) add_subdirectory(clang-query) add_subdirectory(clang-move) Index: clang-tools-extra/trunk/change-namespace/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/change-namespace/CMakeLists.txt +++ clang-tools-extra/trunk/change-namespace/CMakeLists.txt @@ -1,20 +0,0 @@ -set(LLVM_LINK_COMPONENTS - support - ) - -add_clang_library(clangChangeNamespace - ChangeNamespace.cpp - - LINK_LIBS - clangAST - clangASTMatchers - clangBasic - clangFormat - clangFrontend - clangLex - clangSerialization - clangTooling - clangToolingCore - ) - -add_subdirectory(tool) Index: clang-tools-extra/trunk/change-namespace/ChangeNamespace.h =================================================================== --- clang-tools-extra/trunk/change-namespace/ChangeNamespace.h +++ clang-tools-extra/trunk/change-namespace/ChangeNamespace.h @@ -1,175 +0,0 @@ -//===-- ChangeNamespace.h -- Change namespace ------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H -#define LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H - -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Format/Format.h" -#include "clang/Tooling/Core/Replacement.h" -#include "llvm/Support/Regex.h" -#include - -namespace clang { -namespace change_namespace { - -// This tool can be used to change the surrounding namespaces of class/function -// definitions. Classes/functions in the moved namespace will have new -// namespaces while references to symbols (e.g. types, functions) which are not -// defined in the changed namespace will be correctly qualified by prepending -// namespace specifiers before them. -// This will try to add shortest namespace specifiers possible. When a symbol -// reference needs to be fully-qualified, this adds a "::" prefix to the -// namespace specifiers unless the new namespace is the global namespace. -// For classes, only classes that are declared/defined in the given namespace in -// speficifed files will be moved: forward declarations will remain in the old -// namespace. -// For example, changing "a" to "x": -// Old code: -// namespace a { -// class FWD; -// class A { FWD *fwd; } -// } // a -// New code: -// namespace a { -// class FWD; -// } // a -// namespace x { -// class A { ::a::FWD *fwd; } -// } // x -// FIXME: support moving typedef, enums across namespaces. -class ChangeNamespaceTool : public ast_matchers::MatchFinder::MatchCallback { -public: - // Moves code in the old namespace `OldNs` to the new namespace `NewNs` in - // files matching `FilePattern`. - ChangeNamespaceTool( - llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern, - llvm::ArrayRef WhiteListedSymbolPatterns, - std::map *FileToReplacements, - llvm::StringRef FallbackStyle = "LLVM"); - - void registerMatchers(ast_matchers::MatchFinder *Finder); - - void run(const ast_matchers::MatchFinder::MatchResult &Result) override; - - // Moves the changed code in old namespaces but leaves class forward - // declarations behind. - void onEndOfTranslationUnit() override; - -private: - void moveOldNamespace(const ast_matchers::MatchFinder::MatchResult &Result, - const NamespaceDecl *NsDecl); - - void moveClassForwardDeclaration( - const ast_matchers::MatchFinder::MatchResult &Result, - const NamedDecl *FwdDecl); - - void replaceQualifiedSymbolInDeclContext( - const ast_matchers::MatchFinder::MatchResult &Result, - const DeclContext *DeclContext, SourceLocation Start, SourceLocation End, - const NamedDecl *FromDecl); - - void fixTypeLoc(const ast_matchers::MatchFinder::MatchResult &Result, - SourceLocation Start, SourceLocation End, TypeLoc Type); - - void fixUsingShadowDecl(const ast_matchers::MatchFinder::MatchResult &Result, - const UsingDecl *UsingDeclaration); - - void fixDeclRefExpr(const ast_matchers::MatchFinder::MatchResult &Result, - const DeclContext *UseContext, const NamedDecl *From, - const DeclRefExpr *Ref); - - // Information about moving an old namespace. - struct MoveNamespace { - // The start offset of the namespace block being moved in the original - // code. - unsigned Offset; - // The length of the namespace block in the original code. - unsigned Length; - // The offset at which the new namespace block will be inserted in the - // original code. - unsigned InsertionOffset; - // The file in which the namespace is declared. - FileID FID; - SourceManager *SourceMgr; - }; - - // Information about inserting a class forward declaration. - struct InsertForwardDeclaration { - // The offset at while the forward declaration will be inserted in the - // original code. - unsigned InsertionOffset; - // The code to be inserted. - std::string ForwardDeclText; - }; - - std::string FallbackStyle; - // In match callbacks, this contains replacements for replacing `typeLoc`s in - // and deleting forward declarations in the moved namespace blocks. - // In `onEndOfTranslationUnit` callback, the previous added replacements are - // applied (on the moved namespace blocks), and then changed code in old - // namespaces re moved to new namespaces, and previously deleted forward - // declarations are inserted back to old namespaces, from which they are - // deleted. - std::map &FileToReplacements; - // A fully qualified name of the old namespace without "::" prefix, e.g. - // "a::b::c". - std::string OldNamespace; - // A fully qualified name of the new namespace without "::" prefix, e.g. - // "x::y::z". - std::string NewNamespace; - // The longest suffix in the old namespace that does not overlap the new - // namespace. - // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is - // "a::x::y", then `DiffOldNamespace` will be "b::c". - std::string DiffOldNamespace; - // The longest suffix in the new namespace that does not overlap the old - // namespace. - // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is - // "a::x::y", then `DiffNewNamespace` will be "x::y". - std::string DiffNewNamespace; - // A regex pattern that matches files to be processed. - std::string FilePattern; - llvm::Regex FilePatternRE; - // Information about moved namespaces grouped by file. - // Since we are modifying code in old namespaces (e.g. add namespace - // spedifiers) as well as moving them, we store information about namespaces - // to be moved and only move them after all modifications are finished (i.e. - // in `onEndOfTranslationUnit`). - std::map> MoveNamespaces; - // Information about forward declaration insertions grouped by files. - // A class forward declaration is not moved, so it will be deleted from the - // moved code block and inserted back into the old namespace. The insertion - // will be done after removing the code from the old namespace and before - // inserting it to the new namespace. - std::map> InsertFwdDecls; - // Records all using declarations, which can be used to shorten namespace - // specifiers. - llvm::SmallPtrSet UsingDecls; - // Records all using namespace declarations, which can be used to shorten - // namespace specifiers. - llvm::SmallPtrSet UsingNamespaceDecls; - // Records all namespace alias declarations, which can be used to shorten - // namespace specifiers. - llvm::SmallPtrSet NamespaceAliasDecls; - // TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to - // be fixed. - llvm::SmallVector BaseCtorInitializerTypeLocs; - // Since a DeclRefExpr for a function call can be matched twice (one as - // CallExpr and one as DeclRefExpr), we record all DeclRefExpr's that have - // been processed so that we don't handle them twice. - llvm::SmallPtrSet ProcessedFuncRefs; - // Patterns of symbol names whose references are not expected to be updated - // when changing namespaces around them. - std::vector WhiteListedSymbolRegexes; -}; - -} // namespace change_namespace -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H Index: clang-tools-extra/trunk/change-namespace/ChangeNamespace.cpp =================================================================== --- clang-tools-extra/trunk/change-namespace/ChangeNamespace.cpp +++ clang-tools-extra/trunk/change-namespace/ChangeNamespace.cpp @@ -1,1042 +0,0 @@ -//===-- ChangeNamespace.cpp - Change namespace implementation -------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -#include "ChangeNamespace.h" -#include "clang/AST/ASTContext.h" -#include "clang/Format/Format.h" -#include "clang/Lex/Lexer.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/ErrorHandling.h" - -using namespace clang::ast_matchers; - -namespace clang { -namespace change_namespace { - -namespace { - -inline std::string -joinNamespaces(const llvm::SmallVectorImpl &Namespaces) { - if (Namespaces.empty()) - return ""; - std::string Result = Namespaces.front(); - for (auto I = Namespaces.begin() + 1, E = Namespaces.end(); I != E; ++I) - Result += ("::" + *I).str(); - return Result; -} - -// Given "a::b::c", returns {"a", "b", "c"}. -llvm::SmallVector splitSymbolName(llvm::StringRef Name) { - llvm::SmallVector Splitted; - Name.split(Splitted, "::", /*MaxSplit=*/-1, - /*KeepEmpty=*/false); - return Splitted; -} - -SourceLocation startLocationForType(TypeLoc TLoc) { - // For elaborated types (e.g. `struct a::A`) we want the portion after the - // `struct` but including the namespace qualifier, `a::`. - if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) { - NestedNameSpecifierLoc NestedNameSpecifier = - TLoc.castAs().getQualifierLoc(); - if (NestedNameSpecifier.getNestedNameSpecifier()) - return NestedNameSpecifier.getBeginLoc(); - TLoc = TLoc.getNextTypeLoc(); - } - return TLoc.getBeginLoc(); -} - -SourceLocation endLocationForType(TypeLoc TLoc) { - // Dig past any namespace or keyword qualifications. - while (TLoc.getTypeLocClass() == TypeLoc::Elaborated || - TLoc.getTypeLocClass() == TypeLoc::Qualified) - TLoc = TLoc.getNextTypeLoc(); - - // The location for template specializations (e.g. Foo) includes the - // templated types in its location range. We want to restrict this to just - // before the `<` character. - if (TLoc.getTypeLocClass() == TypeLoc::TemplateSpecialization) - return TLoc.castAs() - .getLAngleLoc() - .getLocWithOffset(-1); - return TLoc.getEndLoc(); -} - -// Returns the containing namespace of `InnerNs` by skipping `PartialNsName`. -// If the `InnerNs` does not have `PartialNsName` as suffix, or `PartialNsName` -// is empty, nullptr is returned. -// For example, if `InnerNs` is "a::b::c" and `PartialNsName` is "b::c", then -// the NamespaceDecl of namespace "a" will be returned. -const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs, - llvm::StringRef PartialNsName) { - if (!InnerNs || PartialNsName.empty()) - return nullptr; - const auto *CurrentContext = llvm::cast(InnerNs); - const auto *CurrentNs = InnerNs; - auto PartialNsNameSplitted = splitSymbolName(PartialNsName); - while (!PartialNsNameSplitted.empty()) { - // Get the inner-most namespace in CurrentContext. - while (CurrentContext && !llvm::isa(CurrentContext)) - CurrentContext = CurrentContext->getParent(); - if (!CurrentContext) - return nullptr; - CurrentNs = llvm::cast(CurrentContext); - if (PartialNsNameSplitted.back() != CurrentNs->getNameAsString()) - return nullptr; - PartialNsNameSplitted.pop_back(); - CurrentContext = CurrentContext->getParent(); - } - return CurrentNs; -} - -static std::unique_ptr -getLexerStartingFromLoc(SourceLocation Loc, const SourceManager &SM, - const LangOptions &LangOpts) { - if (Loc.isMacroID() && - !Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc)) - return nullptr; - // Break down the source location. - std::pair LocInfo = SM.getDecomposedLoc(Loc); - // Try to load the file buffer. - bool InvalidTemp = false; - llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp); - if (InvalidTemp) - return nullptr; - - const char *TokBegin = File.data() + LocInfo.second; - // Lex from the start of the given location. - return llvm::make_unique(SM.getLocForStartOfFile(LocInfo.first), - LangOpts, File.begin(), TokBegin, File.end()); -} - -// FIXME: get rid of this helper function if this is supported in clang-refactor -// library. -static SourceLocation getStartOfNextLine(SourceLocation Loc, - const SourceManager &SM, - const LangOptions &LangOpts) { - std::unique_ptr Lex = getLexerStartingFromLoc(Loc, SM, LangOpts); - if (!Lex.get()) - return SourceLocation(); - llvm::SmallVector Line; - // FIXME: this is a bit hacky to get ReadToEndOfLine work. - Lex->setParsingPreprocessorDirective(true); - Lex->ReadToEndOfLine(&Line); - auto End = Loc.getLocWithOffset(Line.size()); - return SM.getLocForEndOfFile(SM.getDecomposedLoc(Loc).first) == End - ? End - : End.getLocWithOffset(1); -} - -// Returns `R` with new range that refers to code after `Replaces` being -// applied. -tooling::Replacement -getReplacementInChangedCode(const tooling::Replacements &Replaces, - const tooling::Replacement &R) { - unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset()); - unsigned NewEnd = - Replaces.getShiftedCodePosition(R.getOffset() + R.getLength()); - return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart, - R.getReplacementText()); -} - -// Adds a replacement `R` into `Replaces` or merges it into `Replaces` by -// applying all existing Replaces first if there is conflict. -void addOrMergeReplacement(const tooling::Replacement &R, - tooling::Replacements *Replaces) { - auto Err = Replaces->add(R); - if (Err) { - llvm::consumeError(std::move(Err)); - auto Replace = getReplacementInChangedCode(*Replaces, R); - *Replaces = Replaces->merge(tooling::Replacements(Replace)); - } -} - -tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End, - llvm::StringRef ReplacementText, - const SourceManager &SM) { - if (!Start.isValid() || !End.isValid()) { - llvm::errs() << "start or end location were invalid\n"; - return tooling::Replacement(); - } - if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) { - llvm::errs() - << "start or end location were in different macro expansions\n"; - return tooling::Replacement(); - } - Start = SM.getSpellingLoc(Start); - End = SM.getSpellingLoc(End); - if (SM.getFileID(Start) != SM.getFileID(End)) { - llvm::errs() << "start or end location were in different files\n"; - return tooling::Replacement(); - } - return tooling::Replacement( - SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start), - SM.getSpellingLoc(End)), - ReplacementText); -} - -void addReplacementOrDie( - SourceLocation Start, SourceLocation End, llvm::StringRef ReplacementText, - const SourceManager &SM, - std::map *FileToReplacements) { - const auto R = createReplacement(Start, End, ReplacementText, SM); - auto Err = (*FileToReplacements)[R.getFilePath()].add(R); - if (Err) - llvm_unreachable(llvm::toString(std::move(Err)).c_str()); -} - -tooling::Replacement createInsertion(SourceLocation Loc, - llvm::StringRef InsertText, - const SourceManager &SM) { - if (Loc.isInvalid()) { - llvm::errs() << "insert Location is invalid.\n"; - return tooling::Replacement(); - } - Loc = SM.getSpellingLoc(Loc); - return tooling::Replacement(SM, Loc, 0, InsertText); -} - -// Returns the shortest qualified name for declaration `DeclName` in the -// namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName` -// is "a::c::d", then "b::X" will be returned. -// Note that if `DeclName` is `::b::X` and `NsName` is `::a::b`, this returns -// "::b::X" instead of "b::X" since there will be a name conflict otherwise. -// \param DeclName A fully qualified name, "::a::b::X" or "a::b::X". -// \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace -// will have empty name. -std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName, - llvm::StringRef NsName) { - DeclName = DeclName.ltrim(':'); - NsName = NsName.ltrim(':'); - if (DeclName.find(':') == llvm::StringRef::npos) - return DeclName; - - auto NsNameSplitted = splitSymbolName(NsName); - auto DeclNsSplitted = splitSymbolName(DeclName); - llvm::StringRef UnqualifiedDeclName = DeclNsSplitted.pop_back_val(); - // If the Decl is in global namespace, there is no need to shorten it. - if (DeclNsSplitted.empty()) - return UnqualifiedDeclName; - // If NsName is the global namespace, we can simply use the DeclName sans - // leading "::". - if (NsNameSplitted.empty()) - return DeclName; - - if (NsNameSplitted.front() != DeclNsSplitted.front()) { - // The DeclName must be fully-qualified, but we still need to decide if a - // leading "::" is necessary. For example, if `NsName` is "a::b::c" and the - // `DeclName` is "b::X", then the reference must be qualified as "::b::X" - // to avoid conflict. - if (llvm::is_contained(NsNameSplitted, DeclNsSplitted.front())) - return ("::" + DeclName).str(); - return DeclName; - } - // Since there is already an overlap namespace, we know that `DeclName` can be - // shortened, so we reduce the longest common prefix. - auto DeclI = DeclNsSplitted.begin(); - auto DeclE = DeclNsSplitted.end(); - auto NsI = NsNameSplitted.begin(); - auto NsE = NsNameSplitted.end(); - for (; DeclI != DeclE && NsI != NsE && *DeclI == *NsI; ++DeclI, ++NsI) { - } - return (DeclI == DeclE) - ? UnqualifiedDeclName.str() - : (llvm::join(DeclI, DeclE, "::") + "::" + UnqualifiedDeclName) - .str(); -} - -std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) { - if (Code.back() != '\n') - Code += "\n"; - auto NsSplitted = splitSymbolName(NestedNs); - while (!NsSplitted.empty()) { - // FIXME: consider code style for comments. - Code = ("namespace " + NsSplitted.back() + " {\n" + Code + - "} // namespace " + NsSplitted.back() + "\n") - .str(); - NsSplitted.pop_back(); - } - return Code; -} - -// Returns true if \p D is a nested DeclContext in \p Context -bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) { - while (D) { - if (D == Context) - return true; - D = D->getParent(); - } - return false; -} - -// Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx. -bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D, - const DeclContext *DeclCtx, SourceLocation Loc) { - SourceLocation DeclLoc = SM.getSpellingLoc(D->getBeginLoc()); - Loc = SM.getSpellingLoc(Loc); - return SM.isBeforeInTranslationUnit(DeclLoc, Loc) && - (SM.getFileID(DeclLoc) == SM.getFileID(Loc) && - isNestedDeclContext(DeclCtx, D->getDeclContext())); -} - -// Given a qualified symbol name, returns true if the symbol will be -// incorrectly qualified without leading "::". For example, a symbol -// "nx::ny::Foo" in namespace "na::nx::ny" without leading "::"; a symbol -// "util::X" in namespace "na" can potentially conflict with "na::util" (if this -// exists). -bool conflictInNamespace(const ASTContext &AST, llvm::StringRef QualifiedSymbol, - llvm::StringRef Namespace) { - auto SymbolSplitted = splitSymbolName(QualifiedSymbol.trim(":")); - assert(!SymbolSplitted.empty()); - SymbolSplitted.pop_back(); // We are only interested in namespaces. - - if (SymbolSplitted.size() >= 1 && !Namespace.empty()) { - auto SymbolTopNs = SymbolSplitted.front(); - auto NsSplitted = splitSymbolName(Namespace.trim(":")); - assert(!NsSplitted.empty()); - - auto LookupDecl = [&AST](const Decl &Scope, - llvm::StringRef Name) -> const NamedDecl * { - const auto *DC = llvm::dyn_cast(&Scope); - if (!DC) - return nullptr; - auto LookupRes = DC->lookup(DeclarationName(&AST.Idents.get(Name))); - if (LookupRes.empty()) - return nullptr; - return LookupRes.front(); - }; - // We do not check the outermost namespace since it would not be a - // conflict if it equals to the symbol's outermost namespace and the - // symbol name would have been shortened. - const NamedDecl *Scope = - LookupDecl(*AST.getTranslationUnitDecl(), NsSplitted.front()); - for (auto I = NsSplitted.begin() + 1, E = NsSplitted.end(); I != E; ++I) { - if (*I == SymbolTopNs) // Handles "::ny" in "::nx::ny" case. - return true; - // Handles "::util" and "::nx::util" conflicts. - if (Scope) { - if (LookupDecl(*Scope, SymbolTopNs)) - return true; - Scope = LookupDecl(*Scope, *I); - } - } - if (Scope && LookupDecl(*Scope, SymbolTopNs)) - return true; - } - return false; -} - -AST_MATCHER(EnumDecl, isScoped) { - return Node.isScoped(); -} - -bool isTemplateParameter(TypeLoc Type) { - while (!Type.isNull()) { - if (Type.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm) - return true; - Type = Type.getNextTypeLoc(); - } - return false; -} - -} // anonymous namespace - -ChangeNamespaceTool::ChangeNamespaceTool( - llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern, - llvm::ArrayRef WhiteListedSymbolPatterns, - std::map *FileToReplacements, - llvm::StringRef FallbackStyle) - : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements), - OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')), - FilePattern(FilePattern), FilePatternRE(FilePattern) { - FileToReplacements->clear(); - auto OldNsSplitted = splitSymbolName(OldNamespace); - auto NewNsSplitted = splitSymbolName(NewNamespace); - // Calculates `DiffOldNamespace` and `DiffNewNamespace`. - while (!OldNsSplitted.empty() && !NewNsSplitted.empty() && - OldNsSplitted.front() == NewNsSplitted.front()) { - OldNsSplitted.erase(OldNsSplitted.begin()); - NewNsSplitted.erase(NewNsSplitted.begin()); - } - DiffOldNamespace = joinNamespaces(OldNsSplitted); - DiffNewNamespace = joinNamespaces(NewNsSplitted); - - for (const auto &Pattern : WhiteListedSymbolPatterns) - WhiteListedSymbolRegexes.emplace_back(Pattern); -} - -void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) { - std::string FullOldNs = "::" + OldNamespace; - // Prefix is the outer-most namespace in DiffOldNamespace. For example, if the - // OldNamespace is "a::b::c" and DiffOldNamespace is "b::c", then Prefix will - // be "a::b". Declarations in this namespace will not be visible in the new - // namespace. If DiffOldNamespace is empty, Prefix will be a invalid name "-". - llvm::SmallVector DiffOldNsSplitted; - llvm::StringRef(DiffOldNamespace) - .split(DiffOldNsSplitted, "::", /*MaxSplit=*/-1, - /*KeepEmpty=*/false); - std::string Prefix = "-"; - if (!DiffOldNsSplitted.empty()) - Prefix = (StringRef(FullOldNs).drop_back(DiffOldNamespace.size()) + - DiffOldNsSplitted.front()) - .str(); - auto IsInMovedNs = - allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")), - isExpansionInFileMatching(FilePattern)); - auto IsVisibleInNewNs = anyOf( - IsInMovedNs, unless(hasAncestor(namespaceDecl(hasName(Prefix))))); - // Match using declarations. - Finder->addMatcher( - usingDecl(isExpansionInFileMatching(FilePattern), IsVisibleInNewNs) - .bind("using"), - this); - // Match using namespace declarations. - Finder->addMatcher(usingDirectiveDecl(isExpansionInFileMatching(FilePattern), - IsVisibleInNewNs) - .bind("using_namespace"), - this); - // Match namespace alias declarations. - Finder->addMatcher(namespaceAliasDecl(isExpansionInFileMatching(FilePattern), - IsVisibleInNewNs) - .bind("namespace_alias"), - this); - - // Match old namespace blocks. - Finder->addMatcher( - namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern)) - .bind("old_ns"), - this); - - // Match class forward-declarations in the old namespace. - // Note that forward-declarations in classes are not matched. - Finder->addMatcher(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())), - IsInMovedNs, hasParent(namespaceDecl())) - .bind("class_fwd_decl"), - this); - - // Match template class forward-declarations in the old namespace. - Finder->addMatcher( - classTemplateDecl(unless(hasDescendant(cxxRecordDecl(isDefinition()))), - IsInMovedNs, hasParent(namespaceDecl())) - .bind("template_class_fwd_decl"), - this); - - // Match references to types that are not defined in the old namespace. - // Forward-declarations in the old namespace are also matched since they will - // be moved back to the old namespace. - auto DeclMatcher = namedDecl( - hasAncestor(namespaceDecl()), - unless(anyOf( - isImplicit(), hasAncestor(namespaceDecl(isAnonymous())), - hasAncestor(cxxRecordDecl()), - allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition()))))))); - - // Using shadow declarations in classes always refers to base class, which - // does not need to be qualified since it can be inferred from inheritance. - // Note that this does not match using alias declarations. - auto UsingShadowDeclInClass = - usingDecl(hasAnyUsingShadowDecl(decl()), hasParent(cxxRecordDecl())); - - // Match TypeLocs on the declaration. Carefully match only the outermost - // TypeLoc and template specialization arguments (which are not outermost) - // that are directly linked to types matching `DeclMatcher`. Nested name - // specifier locs are handled separately below. - Finder->addMatcher( - typeLoc(IsInMovedNs, - loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))), - unless(anyOf(hasParent(typeLoc(loc(qualType( - hasDeclaration(DeclMatcher), - unless(templateSpecializationType()))))), - hasParent(nestedNameSpecifierLoc()), - hasAncestor(isImplicit()), - hasAncestor(UsingShadowDeclInClass), - hasAncestor(functionDecl(isDefaulted())))), - hasAncestor(decl().bind("dc"))) - .bind("type"), - this); - - // Types in `UsingShadowDecl` is not matched by `typeLoc` above, so we need to - // special case it. - // Since using declarations inside classes must have the base class in the - // nested name specifier, we leave it to the nested name specifier matcher. - Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()), - unless(UsingShadowDeclInClass)) - .bind("using_with_shadow"), - this); - - // Handle types in nested name specifier. Specifiers that are in a TypeLoc - // matched above are not matched, e.g. "A::" in "A::A" is not matched since - // "A::A" would have already been fixed. - Finder->addMatcher( - nestedNameSpecifierLoc( - hasAncestor(decl(IsInMovedNs).bind("dc")), - loc(nestedNameSpecifier( - specifiesType(hasDeclaration(DeclMatcher.bind("from_decl"))))), - unless(anyOf(hasAncestor(isImplicit()), - hasAncestor(UsingShadowDeclInClass), - hasAncestor(functionDecl(isDefaulted())), - hasAncestor(typeLoc(loc(qualType(hasDeclaration( - decl(equalsBoundNode("from_decl")))))))))) - .bind("nested_specifier_loc"), - this); - - // Matches base class initializers in constructors. TypeLocs of base class - // initializers do not need to be fixed. For example, - // class X : public a::b::Y { - // public: - // X() : Y::Y() {} // Y::Y do not need namespace specifier. - // }; - Finder->addMatcher( - cxxCtorInitializer(isBaseInitializer()).bind("base_initializer"), this); - - // Handle function. - // Only handle functions that are defined in a namespace excluding member - // function, static methods (qualified by nested specifier), and functions - // defined in the global namespace. - // Note that the matcher does not exclude calls to out-of-line static method - // definitions, so we need to exclude them in the callback handler. - auto FuncMatcher = - functionDecl(unless(anyOf(cxxMethodDecl(), IsInMovedNs, - hasAncestor(namespaceDecl(isAnonymous())), - hasAncestor(cxxRecordDecl()))), - hasParent(namespaceDecl())); - Finder->addMatcher(expr(hasAncestor(decl().bind("dc")), IsInMovedNs, - unless(hasAncestor(isImplicit())), - anyOf(callExpr(callee(FuncMatcher)).bind("call"), - declRefExpr(to(FuncMatcher.bind("func_decl"))) - .bind("func_ref"))), - this); - - auto GlobalVarMatcher = varDecl( - hasGlobalStorage(), hasParent(namespaceDecl()), - unless(anyOf(IsInMovedNs, hasAncestor(namespaceDecl(isAnonymous()))))); - Finder->addMatcher(declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")), - to(GlobalVarMatcher.bind("var_decl"))) - .bind("var_ref"), - this); - - // Handle unscoped enum constant. - auto UnscopedEnumMatcher = enumConstantDecl(hasParent(enumDecl( - hasParent(namespaceDecl()), - unless(anyOf(isScoped(), IsInMovedNs, hasAncestor(cxxRecordDecl()), - hasAncestor(namespaceDecl(isAnonymous()))))))); - Finder->addMatcher( - declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")), - to(UnscopedEnumMatcher.bind("enum_const_decl"))) - .bind("enum_const_ref"), - this); -} - -void ChangeNamespaceTool::run( - const ast_matchers::MatchFinder::MatchResult &Result) { - if (const auto *Using = Result.Nodes.getNodeAs("using")) { - UsingDecls.insert(Using); - } else if (const auto *UsingNamespace = - Result.Nodes.getNodeAs( - "using_namespace")) { - UsingNamespaceDecls.insert(UsingNamespace); - } else if (const auto *NamespaceAlias = - Result.Nodes.getNodeAs( - "namespace_alias")) { - NamespaceAliasDecls.insert(NamespaceAlias); - } else if (const auto *NsDecl = - Result.Nodes.getNodeAs("old_ns")) { - moveOldNamespace(Result, NsDecl); - } else if (const auto *FwdDecl = - Result.Nodes.getNodeAs("class_fwd_decl")) { - moveClassForwardDeclaration(Result, cast(FwdDecl)); - } else if (const auto *TemplateFwdDecl = - Result.Nodes.getNodeAs( - "template_class_fwd_decl")) { - moveClassForwardDeclaration(Result, cast(TemplateFwdDecl)); - } else if (const auto *UsingWithShadow = - Result.Nodes.getNodeAs("using_with_shadow")) { - fixUsingShadowDecl(Result, UsingWithShadow); - } else if (const auto *Specifier = - Result.Nodes.getNodeAs( - "nested_specifier_loc")) { - SourceLocation Start = Specifier->getBeginLoc(); - SourceLocation End = endLocationForType(Specifier->getTypeLoc()); - fixTypeLoc(Result, Start, End, Specifier->getTypeLoc()); - } else if (const auto *BaseInitializer = - Result.Nodes.getNodeAs( - "base_initializer")) { - BaseCtorInitializerTypeLocs.push_back( - BaseInitializer->getTypeSourceInfo()->getTypeLoc()); - } else if (const auto *TLoc = Result.Nodes.getNodeAs("type")) { - // This avoids fixing types with record types as qualifier, which is not - // filtered by matchers in some cases, e.g. the type is templated. We should - // handle the record type qualifier instead. - TypeLoc Loc = *TLoc; - while (Loc.getTypeLocClass() == TypeLoc::Qualified) - Loc = Loc.getNextTypeLoc(); - if (Loc.getTypeLocClass() == TypeLoc::Elaborated) { - NestedNameSpecifierLoc NestedNameSpecifier = - Loc.castAs().getQualifierLoc(); - // This happens for friend declaration of a base class with injected class - // name. - if (!NestedNameSpecifier.getNestedNameSpecifier()) - return; - const Type *SpecifierType = - NestedNameSpecifier.getNestedNameSpecifier()->getAsType(); - if (SpecifierType && SpecifierType->isRecordType()) - return; - } - fixTypeLoc(Result, startLocationForType(Loc), endLocationForType(Loc), Loc); - } else if (const auto *VarRef = - Result.Nodes.getNodeAs("var_ref")) { - const auto *Var = Result.Nodes.getNodeAs("var_decl"); - assert(Var); - if (Var->getCanonicalDecl()->isStaticDataMember()) - return; - const auto *Context = Result.Nodes.getNodeAs("dc"); - assert(Context && "Empty decl context."); - fixDeclRefExpr(Result, Context->getDeclContext(), - llvm::cast(Var), VarRef); - } else if (const auto *EnumConstRef = - Result.Nodes.getNodeAs("enum_const_ref")) { - // Do not rename the reference if it is already scoped by the EnumDecl name. - if (EnumConstRef->hasQualifier() && - EnumConstRef->getQualifier()->getKind() == - NestedNameSpecifier::SpecifierKind::TypeSpec && - EnumConstRef->getQualifier()->getAsType()->isEnumeralType()) - return; - const auto *EnumConstDecl = - Result.Nodes.getNodeAs("enum_const_decl"); - assert(EnumConstDecl); - const auto *Context = Result.Nodes.getNodeAs("dc"); - assert(Context && "Empty decl context."); - // FIXME: this would qualify "ns::VALUE" as "ns::EnumValue::VALUE". Fix it - // if it turns out to be an issue. - fixDeclRefExpr(Result, Context->getDeclContext(), - llvm::cast(EnumConstDecl), EnumConstRef); - } else if (const auto *FuncRef = - Result.Nodes.getNodeAs("func_ref")) { - // If this reference has been processed as a function call, we do not - // process it again. - if (ProcessedFuncRefs.count(FuncRef)) - return; - ProcessedFuncRefs.insert(FuncRef); - const auto *Func = Result.Nodes.getNodeAs("func_decl"); - assert(Func); - const auto *Context = Result.Nodes.getNodeAs("dc"); - assert(Context && "Empty decl context."); - fixDeclRefExpr(Result, Context->getDeclContext(), - llvm::cast(Func), FuncRef); - } else { - const auto *Call = Result.Nodes.getNodeAs("call"); - assert(Call != nullptr && "Expecting callback for CallExpr."); - const auto *CalleeFuncRef = - llvm::cast(Call->getCallee()->IgnoreImplicit()); - ProcessedFuncRefs.insert(CalleeFuncRef); - const FunctionDecl *Func = Call->getDirectCallee(); - assert(Func != nullptr); - // FIXME: ignore overloaded operators. This would miss cases where operators - // are called by qualified names (i.e. "ns::operator <"). Ignore such - // cases for now. - if (Func->isOverloadedOperator()) - return; - // Ignore out-of-line static methods since they will be handled by nested - // name specifiers. - if (Func->getCanonicalDecl()->getStorageClass() == - StorageClass::SC_Static && - Func->isOutOfLine()) - return; - const auto *Context = Result.Nodes.getNodeAs("dc"); - assert(Context && "Empty decl context."); - SourceRange CalleeRange = Call->getCallee()->getSourceRange(); - replaceQualifiedSymbolInDeclContext( - Result, Context->getDeclContext(), CalleeRange.getBegin(), - CalleeRange.getEnd(), llvm::cast(Func)); - } -} - -static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl, - const SourceManager &SM, - const LangOptions &LangOpts) { - std::unique_ptr Lex = - getLexerStartingFromLoc(NsDecl->getBeginLoc(), SM, LangOpts); - assert(Lex.get() && - "Failed to create lexer from the beginning of namespace."); - if (!Lex.get()) - return SourceLocation(); - Token Tok; - while (!Lex->LexFromRawLexer(Tok) && Tok.isNot(tok::TokenKind::l_brace)) { - } - return Tok.isNot(tok::TokenKind::l_brace) - ? SourceLocation() - : Tok.getEndLoc().getLocWithOffset(1); -} - -// Stores information about a moved namespace in `MoveNamespaces` and leaves -// the actual movement to `onEndOfTranslationUnit()`. -void ChangeNamespaceTool::moveOldNamespace( - const ast_matchers::MatchFinder::MatchResult &Result, - const NamespaceDecl *NsDecl) { - // If the namespace is empty, do nothing. - if (Decl::castToDeclContext(NsDecl)->decls_empty()) - return; - - const SourceManager &SM = *Result.SourceManager; - // Get the range of the code in the old namespace. - SourceLocation Start = - getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()); - assert(Start.isValid() && "Can't find l_brace for namespace."); - MoveNamespace MoveNs; - MoveNs.Offset = SM.getFileOffset(Start); - // The range of the moved namespace is from the location just past the left - // brace to the location right before the right brace. - MoveNs.Length = SM.getFileOffset(NsDecl->getRBraceLoc()) - MoveNs.Offset; - - // Insert the new namespace after `DiffOldNamespace`. For example, if - // `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then - // "x::y" will be inserted inside the existing namespace "a" and after "a::b". - // `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b" - // in the above example. - // If there is no outer namespace (i.e. DiffOldNamespace is empty), the new - // namespace will be a nested namespace in the old namespace. - const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace); - SourceLocation InsertionLoc = Start; - if (OuterNs) { - SourceLocation LocAfterNs = getStartOfNextLine( - OuterNs->getRBraceLoc(), SM, Result.Context->getLangOpts()); - assert(LocAfterNs.isValid() && - "Failed to get location after DiffOldNamespace"); - InsertionLoc = LocAfterNs; - } - MoveNs.InsertionOffset = SM.getFileOffset(SM.getSpellingLoc(InsertionLoc)); - MoveNs.FID = SM.getFileID(Start); - MoveNs.SourceMgr = Result.SourceManager; - MoveNamespaces[SM.getFilename(Start)].push_back(MoveNs); -} - -// Removes a class forward declaration from the code in the moved namespace and -// creates an `InsertForwardDeclaration` to insert the forward declaration back -// into the old namespace after moving code from the old namespace to the new -// namespace. -// For example, changing "a" to "x": -// Old code: -// namespace a { -// class FWD; -// class A { FWD *fwd; } -// } // a -// New code: -// namespace a { -// class FWD; -// } // a -// namespace x { -// class A { a::FWD *fwd; } -// } // x -void ChangeNamespaceTool::moveClassForwardDeclaration( - const ast_matchers::MatchFinder::MatchResult &Result, - const NamedDecl *FwdDecl) { - SourceLocation Start = FwdDecl->getBeginLoc(); - SourceLocation End = FwdDecl->getEndLoc(); - const SourceManager &SM = *Result.SourceManager; - SourceLocation AfterSemi = Lexer::findLocationAfterToken( - End, tok::semi, SM, Result.Context->getLangOpts(), - /*SkipTrailingWhitespaceAndNewLine=*/true); - if (AfterSemi.isValid()) - End = AfterSemi.getLocWithOffset(-1); - // Delete the forward declaration from the code to be moved. - addReplacementOrDie(Start, End, "", SM, &FileToReplacements); - llvm::StringRef Code = Lexer::getSourceText( - CharSourceRange::getTokenRange(SM.getSpellingLoc(Start), - SM.getSpellingLoc(End)), - SM, Result.Context->getLangOpts()); - // Insert the forward declaration back into the old namespace after moving the - // code from old namespace to new namespace. - // Insertion information is stored in `InsertFwdDecls` and actual - // insertion will be performed in `onEndOfTranslationUnit`. - // Get the (old) namespace that contains the forward declaration. - const auto *NsDecl = Result.Nodes.getNodeAs("ns_decl"); - // The namespace contains the forward declaration, so it must not be empty. - assert(!NsDecl->decls_empty()); - const auto Insertion = createInsertion( - getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()), - Code, SM); - InsertForwardDeclaration InsertFwd; - InsertFwd.InsertionOffset = Insertion.getOffset(); - InsertFwd.ForwardDeclText = Insertion.getReplacementText().str(); - InsertFwdDecls[Insertion.getFilePath()].push_back(InsertFwd); -} - -// Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p -// FromDecl with the shortest qualified name possible when the reference is in -// `NewNamespace`. -void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext( - const ast_matchers::MatchFinder::MatchResult &Result, - const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End, - const NamedDecl *FromDecl) { - const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext(); - if (llvm::isa(NsDeclContext)) { - // This should not happen in usual unless the TypeLoc is in function type - // parameters, e.g `std::function`. In this case, DeclContext of - // `T` will be the translation unit. We simply use fully-qualified name - // here. - // Note that `FromDecl` must not be defined in the old namespace (according - // to `DeclMatcher`), so its fully-qualified name will not change after - // changing the namespace. - addReplacementOrDie(Start, End, FromDecl->getQualifiedNameAsString(), - *Result.SourceManager, &FileToReplacements); - return; - } - const auto *NsDecl = llvm::cast(NsDeclContext); - // Calculate the name of the `NsDecl` after it is moved to new namespace. - std::string OldNs = NsDecl->getQualifiedNameAsString(); - llvm::StringRef Postfix = OldNs; - bool Consumed = Postfix.consume_front(OldNamespace); - assert(Consumed && "Expect OldNS to start with OldNamespace."); - (void)Consumed; - const std::string NewNs = (NewNamespace + Postfix).str(); - - llvm::StringRef NestedName = Lexer::getSourceText( - CharSourceRange::getTokenRange( - Result.SourceManager->getSpellingLoc(Start), - Result.SourceManager->getSpellingLoc(End)), - *Result.SourceManager, Result.Context->getLangOpts()); - std::string FromDeclName = FromDecl->getQualifiedNameAsString(); - for (llvm::Regex &RE : WhiteListedSymbolRegexes) - if (RE.match(FromDeclName)) - return; - std::string ReplaceName = - getShortestQualifiedNameInNamespace(FromDeclName, NewNs); - // Checks if there is any using namespace declarations that can shorten the - // qualified name. - for (const auto *UsingNamespace : UsingNamespaceDecls) { - if (!isDeclVisibleAtLocation(*Result.SourceManager, UsingNamespace, DeclCtx, - Start)) - continue; - StringRef FromDeclNameRef = FromDeclName; - if (FromDeclNameRef.consume_front(UsingNamespace->getNominatedNamespace() - ->getQualifiedNameAsString())) { - FromDeclNameRef = FromDeclNameRef.drop_front(2); - if (FromDeclNameRef.size() < ReplaceName.size()) - ReplaceName = FromDeclNameRef; - } - } - // Checks if there is any namespace alias declarations that can shorten the - // qualified name. - for (const auto *NamespaceAlias : NamespaceAliasDecls) { - if (!isDeclVisibleAtLocation(*Result.SourceManager, NamespaceAlias, DeclCtx, - Start)) - continue; - StringRef FromDeclNameRef = FromDeclName; - if (FromDeclNameRef.consume_front( - NamespaceAlias->getNamespace()->getQualifiedNameAsString() + - "::")) { - std::string AliasName = NamespaceAlias->getNameAsString(); - std::string AliasQualifiedName = - NamespaceAlias->getQualifiedNameAsString(); - // We only consider namespace aliases define in the global namepspace or - // in namespaces that are directly visible from the reference, i.e. - // ancestor of the `OldNs`. Note that declarations in ancestor namespaces - // but not visible in the new namespace is filtered out by - // "IsVisibleInNewNs" matcher. - if (AliasQualifiedName != AliasName) { - // The alias is defined in some namespace. - assert(StringRef(AliasQualifiedName).endswith("::" + AliasName)); - llvm::StringRef AliasNs = - StringRef(AliasQualifiedName).drop_back(AliasName.size() + 2); - if (!llvm::StringRef(OldNs).startswith(AliasNs)) - continue; - } - std::string NameWithAliasNamespace = - (AliasName + "::" + FromDeclNameRef).str(); - if (NameWithAliasNamespace.size() < ReplaceName.size()) - ReplaceName = NameWithAliasNamespace; - } - } - // Checks if there is any using shadow declarations that can shorten the - // qualified name. - bool Matched = false; - for (const UsingDecl *Using : UsingDecls) { - if (Matched) - break; - if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) { - for (const auto *UsingShadow : Using->shadows()) { - const auto *TargetDecl = UsingShadow->getTargetDecl(); - if (TargetDecl->getQualifiedNameAsString() == - FromDecl->getQualifiedNameAsString()) { - ReplaceName = FromDecl->getNameAsString(); - Matched = true; - break; - } - } - } - } - bool Conflict = conflictInNamespace(DeclCtx->getParentASTContext(), - ReplaceName, NewNamespace); - // If the new nested name in the new namespace is the same as it was in the - // old namespace, we don't create replacement unless there can be ambiguity. - if ((NestedName == ReplaceName && !Conflict) || - (NestedName.startswith("::") && NestedName.drop_front(2) == ReplaceName)) - return; - // If the reference need to be fully-qualified, add a leading "::" unless - // NewNamespace is the global namespace. - if (ReplaceName == FromDeclName && !NewNamespace.empty() && Conflict) - ReplaceName = "::" + ReplaceName; - addReplacementOrDie(Start, End, ReplaceName, *Result.SourceManager, - &FileToReplacements); -} - -// Replace the [Start, End] of `Type` with the shortest qualified name when the -// `Type` is in `NewNamespace`. -void ChangeNamespaceTool::fixTypeLoc( - const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start, - SourceLocation End, TypeLoc Type) { - // FIXME: do not rename template parameter. - if (Start.isInvalid() || End.isInvalid()) - return; - // Types of CXXCtorInitializers do not need to be fixed. - if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type)) - return; - if (isTemplateParameter(Type)) - return; - // The declaration which this TypeLoc refers to. - const auto *FromDecl = Result.Nodes.getNodeAs("from_decl"); - // `hasDeclaration` gives underlying declaration, but if the type is - // a typedef type, we need to use the typedef type instead. - auto IsInMovedNs = [&](const NamedDecl *D) { - if (!llvm::StringRef(D->getQualifiedNameAsString()) - .startswith(OldNamespace + "::")) - return false; - auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getBeginLoc()); - if (ExpansionLoc.isInvalid()) - return false; - llvm::StringRef Filename = Result.SourceManager->getFilename(ExpansionLoc); - return FilePatternRE.match(Filename); - }; - // Make `FromDecl` the immediate declaration that `Type` refers to, i.e. if - // `Type` is an alias type, we make `FromDecl` the type alias declaration. - // Also, don't fix the \p Type if it refers to a type alias decl in the moved - // namespace since the alias decl will be moved along with the type reference. - if (auto *Typedef = Type.getType()->getAs()) { - FromDecl = Typedef->getDecl(); - if (IsInMovedNs(FromDecl)) - return; - } else if (auto *TemplateType = - Type.getType()->getAs()) { - if (TemplateType->isTypeAlias()) { - FromDecl = TemplateType->getTemplateName().getAsTemplateDecl(); - if (IsInMovedNs(FromDecl)) - return; - } - } - const auto *DeclCtx = Result.Nodes.getNodeAs("dc"); - assert(DeclCtx && "Empty decl context."); - replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start, - End, FromDecl); -} - -void ChangeNamespaceTool::fixUsingShadowDecl( - const ast_matchers::MatchFinder::MatchResult &Result, - const UsingDecl *UsingDeclaration) { - SourceLocation Start = UsingDeclaration->getBeginLoc(); - SourceLocation End = UsingDeclaration->getEndLoc(); - if (Start.isInvalid() || End.isInvalid()) - return; - - assert(UsingDeclaration->shadow_size() > 0); - // FIXME: it might not be always accurate to use the first using-decl. - const NamedDecl *TargetDecl = - UsingDeclaration->shadow_begin()->getTargetDecl(); - std::string TargetDeclName = TargetDecl->getQualifiedNameAsString(); - // FIXME: check if target_decl_name is in moved ns, which doesn't make much - // sense. If this happens, we need to use name with the new namespace. - // Use fully qualified name in UsingDecl for now. - addReplacementOrDie(Start, End, "using ::" + TargetDeclName, - *Result.SourceManager, &FileToReplacements); -} - -void ChangeNamespaceTool::fixDeclRefExpr( - const ast_matchers::MatchFinder::MatchResult &Result, - const DeclContext *UseContext, const NamedDecl *From, - const DeclRefExpr *Ref) { - SourceRange RefRange = Ref->getSourceRange(); - replaceQualifiedSymbolInDeclContext(Result, UseContext, RefRange.getBegin(), - RefRange.getEnd(), From); -} - -void ChangeNamespaceTool::onEndOfTranslationUnit() { - // Move namespace blocks and insert forward declaration to old namespace. - for (const auto &FileAndNsMoves : MoveNamespaces) { - auto &NsMoves = FileAndNsMoves.second; - if (NsMoves.empty()) - continue; - const std::string &FilePath = FileAndNsMoves.first; - auto &Replaces = FileToReplacements[FilePath]; - auto &SM = *NsMoves.begin()->SourceMgr; - llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID); - auto ChangedCode = tooling::applyAllReplacements(Code, Replaces); - if (!ChangedCode) { - llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n"; - continue; - } - // Replacements on the changed code for moving namespaces and inserting - // forward declarations to old namespaces. - tooling::Replacements NewReplacements; - // Cut the changed code from the old namespace and paste the code in the new - // namespace. - for (const auto &NsMove : NsMoves) { - // Calculate the range of the old namespace block in the changed - // code. - const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset); - const unsigned NewLength = - Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) - - NewOffset; - tooling::Replacement Deletion(FilePath, NewOffset, NewLength, ""); - std::string MovedCode = ChangedCode->substr(NewOffset, NewLength); - std::string MovedCodeWrappedInNewNs = - wrapCodeInNamespace(DiffNewNamespace, MovedCode); - // Calculate the new offset at which the code will be inserted in the - // changed code. - unsigned NewInsertionOffset = - Replaces.getShiftedCodePosition(NsMove.InsertionOffset); - tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0, - MovedCodeWrappedInNewNs); - addOrMergeReplacement(Deletion, &NewReplacements); - addOrMergeReplacement(Insertion, &NewReplacements); - } - // After moving namespaces, insert forward declarations back to old - // namespaces. - const auto &FwdDeclInsertions = InsertFwdDecls[FilePath]; - for (const auto &FwdDeclInsertion : FwdDeclInsertions) { - unsigned NewInsertionOffset = - Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset); - tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0, - FwdDeclInsertion.ForwardDeclText); - addOrMergeReplacement(Insertion, &NewReplacements); - } - // Add replacements referring to the changed code to existing replacements, - // which refers to the original code. - Replaces = Replaces.merge(NewReplacements); - auto Style = - format::getStyle(format::DefaultFormatStyle, FilePath, FallbackStyle); - if (!Style) { - llvm::errs() << llvm::toString(Style.takeError()) << "\n"; - continue; - } - // Clean up old namespaces if there is nothing in it after moving. - auto CleanReplacements = - format::cleanupAroundReplacements(Code, Replaces, *Style); - if (!CleanReplacements) { - llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n"; - continue; - } - FileToReplacements[FilePath] = *CleanReplacements; - } - - // Make sure we don't generate replacements for files that do not match - // FilePattern. - for (auto &Entry : FileToReplacements) - if (!FilePatternRE.match(Entry.first)) - Entry.second.clear(); -} - -} // namespace change_namespace -} // namespace clang Index: clang-tools-extra/trunk/change-namespace/tool/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/change-namespace/tool/CMakeLists.txt +++ clang-tools-extra/trunk/change-namespace/tool/CMakeLists.txt @@ -1,25 +0,0 @@ -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) - -set(LLVM_LINK_COMPONENTS - Support - ) - -add_clang_executable(clang-change-namespace - ClangChangeNamespace.cpp - ) -target_link_libraries(clang-change-namespace - PRIVATE - clangAST - clangASTMatchers - clangBasic - clangChangeNamespace - clangFormat - clangFrontend - clangRewrite - clangSerialization - clangTooling - clangToolingCore - ) - -install(TARGETS clang-change-namespace - RUNTIME DESTINATION bin) Index: clang-tools-extra/trunk/change-namespace/tool/ClangChangeNamespace.cpp =================================================================== --- clang-tools-extra/trunk/change-namespace/tool/ClangChangeNamespace.cpp +++ clang-tools-extra/trunk/change-namespace/tool/ClangChangeNamespace.cpp @@ -1,177 +0,0 @@ -//===-- ClangChangeNamespace.cpp - Standalone change namespace ------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// This tool can be used to change the surrounding namespaces of class/function -// definitions. -// -// Example: test.cc -// namespace na { -// class X {}; -// namespace nb { -// class Y { X x; }; -// } // namespace nb -// } // namespace na -// To move the definition of class Y from namespace "na::nb" to "x::y", run: -// clang-change-namespace --old_namespace "na::nb" \ -// --new_namespace "x::y" --file_pattern "test.cc" test.cc -- -// Output: -// namespace na { -// class X {}; -// } // namespace na -// namespace x { -// namespace y { -// class Y { na::X x; }; -// } // namespace y -// } // namespace x - -#include "ChangeNamespace.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Frontend/FrontendActions.h" -#include "clang/Frontend/TextDiagnosticPrinter.h" -#include "clang/Rewrite/Core/Rewriter.h" -#include "clang/Tooling/CommonOptionsParser.h" -#include "clang/Tooling/Refactoring.h" -#include "clang/Tooling/Tooling.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Signals.h" -#include "llvm/Support/YAMLTraits.h" - -using namespace clang; -using namespace llvm; - -namespace { - -cl::OptionCategory ChangeNamespaceCategory("Change namespace."); - -cl::opt OldNamespace("old_namespace", cl::Required, - cl::desc("Old namespace."), - cl::cat(ChangeNamespaceCategory)); - -cl::opt NewNamespace("new_namespace", cl::Required, - cl::desc("New namespace."), - cl::cat(ChangeNamespaceCategory)); - -cl::opt FilePattern( - "file_pattern", cl::Required, - cl::desc("Only rename namespaces in files that match the given pattern."), - cl::cat(ChangeNamespaceCategory)); - -cl::opt Inplace("i", cl::desc("Inplace edit s, if specified."), - cl::cat(ChangeNamespaceCategory)); - -cl::opt - DumpYAML("dump_result", - cl::desc("Dump new file contents in YAML, if specified."), - cl::cat(ChangeNamespaceCategory)); - -cl::opt Style("style", - cl::desc("The style name used for reformatting."), - cl::init("LLVM"), cl::cat(ChangeNamespaceCategory)); - -cl::opt WhiteListFile( - "whitelist_file", - cl::desc("A file containing regexes of symbol names that are not expected " - "to be updated when changing namespaces around them."), - cl::init(""), cl::cat(ChangeNamespaceCategory)); - -llvm::ErrorOr> GetWhiteListedSymbolPatterns() { - std::vector Patterns; - if (WhiteListFile.empty()) - return Patterns; - - llvm::SmallVector Lines; - llvm::ErrorOr> File = - llvm::MemoryBuffer::getFile(WhiteListFile); - if (!File) - return File.getError(); - llvm::StringRef Content = File.get()->getBuffer(); - Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); - for (auto Line : Lines) - Patterns.push_back(Line.trim()); - return Patterns; -} - -} // anonymous namespace - -int main(int argc, const char **argv) { - llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); - tooling::CommonOptionsParser OptionsParser(argc, argv, - ChangeNamespaceCategory); - const auto &Files = OptionsParser.getSourcePathList(); - tooling::RefactoringTool Tool(OptionsParser.getCompilations(), Files); - llvm::ErrorOr> WhiteListPatterns = - GetWhiteListedSymbolPatterns(); - if (!WhiteListPatterns) { - llvm::errs() << "Failed to open whitelist file " << WhiteListFile << ". " - << WhiteListPatterns.getError().message() << "\n"; - return 1; - } - change_namespace::ChangeNamespaceTool NamespaceTool( - OldNamespace, NewNamespace, FilePattern, *WhiteListPatterns, - &Tool.getReplacements(), Style); - ast_matchers::MatchFinder Finder; - NamespaceTool.registerMatchers(&Finder); - std::unique_ptr Factory = - tooling::newFrontendActionFactory(&Finder); - - if (int Result = Tool.run(Factory.get())) - return Result; - LangOptions DefaultLangOptions; - IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); - clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts); - DiagnosticsEngine Diagnostics( - IntrusiveRefCntPtr(new DiagnosticIDs()), &*DiagOpts, - &DiagnosticPrinter, false); - auto &FileMgr = Tool.getFiles(); - SourceManager Sources(Diagnostics, FileMgr); - Rewriter Rewrite(Sources, DefaultLangOptions); - - if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) { - llvm::errs() << "Failed applying all replacements.\n"; - return 1; - } - if (Inplace) - return Rewrite.overwriteChangedFiles(); - - std::set ChangedFiles; - for (const auto &it : Tool.getReplacements()) - ChangedFiles.insert(it.first); - - if (DumpYAML) { - auto WriteToYAML = [&](llvm::raw_ostream &OS) { - OS << "[\n"; - for (auto I = ChangedFiles.begin(), E = ChangedFiles.end(); I != E; ++I) { - OS << " {\n"; - OS << " \"FilePath\": \"" << *I << "\",\n"; - const auto *Entry = FileMgr.getFile(*I); - auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User); - std::string Content; - llvm::raw_string_ostream ContentStream(Content); - Rewrite.getEditBuffer(ID).write(ContentStream); - OS << " \"SourceText\": \"" - << llvm::yaml::escape(ContentStream.str()) << "\"\n"; - OS << " }"; - if (I != std::prev(E)) - OS << ",\n"; - } - OS << "\n]\n"; - }; - WriteToYAML(llvm::outs()); - return 0; - } - - for (const auto &File : ChangedFiles) { - const auto *Entry = FileMgr.getFile(File); - - auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User); - outs() << "============== " << File << " ==============\n"; - Rewrite.getEditBuffer(ID).write(llvm::outs()); - outs() << "\n============================================\n"; - } - - return 0; -} Index: clang-tools-extra/trunk/clang-change-namespace/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-change-namespace/CMakeLists.txt +++ clang-tools-extra/trunk/clang-change-namespace/CMakeLists.txt @@ -0,0 +1,20 @@ +set(LLVM_LINK_COMPONENTS + support + ) + +add_clang_library(clangChangeNamespace + ChangeNamespace.cpp + + LINK_LIBS + clangAST + clangASTMatchers + clangBasic + clangFormat + clangFrontend + clangLex + clangSerialization + clangTooling + clangToolingCore + ) + +add_subdirectory(tool) Index: clang-tools-extra/trunk/clang-change-namespace/ChangeNamespace.h =================================================================== --- clang-tools-extra/trunk/clang-change-namespace/ChangeNamespace.h +++ clang-tools-extra/trunk/clang-change-namespace/ChangeNamespace.h @@ -0,0 +1,175 @@ +//===-- ChangeNamespace.h -- Change namespace ------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H +#define LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Format/Format.h" +#include "clang/Tooling/Core/Replacement.h" +#include "llvm/Support/Regex.h" +#include + +namespace clang { +namespace change_namespace { + +// This tool can be used to change the surrounding namespaces of class/function +// definitions. Classes/functions in the moved namespace will have new +// namespaces while references to symbols (e.g. types, functions) which are not +// defined in the changed namespace will be correctly qualified by prepending +// namespace specifiers before them. +// This will try to add shortest namespace specifiers possible. When a symbol +// reference needs to be fully-qualified, this adds a "::" prefix to the +// namespace specifiers unless the new namespace is the global namespace. +// For classes, only classes that are declared/defined in the given namespace in +// speficifed files will be moved: forward declarations will remain in the old +// namespace. +// For example, changing "a" to "x": +// Old code: +// namespace a { +// class FWD; +// class A { FWD *fwd; } +// } // a +// New code: +// namespace a { +// class FWD; +// } // a +// namespace x { +// class A { ::a::FWD *fwd; } +// } // x +// FIXME: support moving typedef, enums across namespaces. +class ChangeNamespaceTool : public ast_matchers::MatchFinder::MatchCallback { +public: + // Moves code in the old namespace `OldNs` to the new namespace `NewNs` in + // files matching `FilePattern`. + ChangeNamespaceTool( + llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern, + llvm::ArrayRef WhiteListedSymbolPatterns, + std::map *FileToReplacements, + llvm::StringRef FallbackStyle = "LLVM"); + + void registerMatchers(ast_matchers::MatchFinder *Finder); + + void run(const ast_matchers::MatchFinder::MatchResult &Result) override; + + // Moves the changed code in old namespaces but leaves class forward + // declarations behind. + void onEndOfTranslationUnit() override; + +private: + void moveOldNamespace(const ast_matchers::MatchFinder::MatchResult &Result, + const NamespaceDecl *NsDecl); + + void moveClassForwardDeclaration( + const ast_matchers::MatchFinder::MatchResult &Result, + const NamedDecl *FwdDecl); + + void replaceQualifiedSymbolInDeclContext( + const ast_matchers::MatchFinder::MatchResult &Result, + const DeclContext *DeclContext, SourceLocation Start, SourceLocation End, + const NamedDecl *FromDecl); + + void fixTypeLoc(const ast_matchers::MatchFinder::MatchResult &Result, + SourceLocation Start, SourceLocation End, TypeLoc Type); + + void fixUsingShadowDecl(const ast_matchers::MatchFinder::MatchResult &Result, + const UsingDecl *UsingDeclaration); + + void fixDeclRefExpr(const ast_matchers::MatchFinder::MatchResult &Result, + const DeclContext *UseContext, const NamedDecl *From, + const DeclRefExpr *Ref); + + // Information about moving an old namespace. + struct MoveNamespace { + // The start offset of the namespace block being moved in the original + // code. + unsigned Offset; + // The length of the namespace block in the original code. + unsigned Length; + // The offset at which the new namespace block will be inserted in the + // original code. + unsigned InsertionOffset; + // The file in which the namespace is declared. + FileID FID; + SourceManager *SourceMgr; + }; + + // Information about inserting a class forward declaration. + struct InsertForwardDeclaration { + // The offset at while the forward declaration will be inserted in the + // original code. + unsigned InsertionOffset; + // The code to be inserted. + std::string ForwardDeclText; + }; + + std::string FallbackStyle; + // In match callbacks, this contains replacements for replacing `typeLoc`s in + // and deleting forward declarations in the moved namespace blocks. + // In `onEndOfTranslationUnit` callback, the previous added replacements are + // applied (on the moved namespace blocks), and then changed code in old + // namespaces re moved to new namespaces, and previously deleted forward + // declarations are inserted back to old namespaces, from which they are + // deleted. + std::map &FileToReplacements; + // A fully qualified name of the old namespace without "::" prefix, e.g. + // "a::b::c". + std::string OldNamespace; + // A fully qualified name of the new namespace without "::" prefix, e.g. + // "x::y::z". + std::string NewNamespace; + // The longest suffix in the old namespace that does not overlap the new + // namespace. + // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is + // "a::x::y", then `DiffOldNamespace` will be "b::c". + std::string DiffOldNamespace; + // The longest suffix in the new namespace that does not overlap the old + // namespace. + // For example, if `OldNamespace` is "a::b::c" and `NewNamespace` is + // "a::x::y", then `DiffNewNamespace` will be "x::y". + std::string DiffNewNamespace; + // A regex pattern that matches files to be processed. + std::string FilePattern; + llvm::Regex FilePatternRE; + // Information about moved namespaces grouped by file. + // Since we are modifying code in old namespaces (e.g. add namespace + // spedifiers) as well as moving them, we store information about namespaces + // to be moved and only move them after all modifications are finished (i.e. + // in `onEndOfTranslationUnit`). + std::map> MoveNamespaces; + // Information about forward declaration insertions grouped by files. + // A class forward declaration is not moved, so it will be deleted from the + // moved code block and inserted back into the old namespace. The insertion + // will be done after removing the code from the old namespace and before + // inserting it to the new namespace. + std::map> InsertFwdDecls; + // Records all using declarations, which can be used to shorten namespace + // specifiers. + llvm::SmallPtrSet UsingDecls; + // Records all using namespace declarations, which can be used to shorten + // namespace specifiers. + llvm::SmallPtrSet UsingNamespaceDecls; + // Records all namespace alias declarations, which can be used to shorten + // namespace specifiers. + llvm::SmallPtrSet NamespaceAliasDecls; + // TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to + // be fixed. + llvm::SmallVector BaseCtorInitializerTypeLocs; + // Since a DeclRefExpr for a function call can be matched twice (one as + // CallExpr and one as DeclRefExpr), we record all DeclRefExpr's that have + // been processed so that we don't handle them twice. + llvm::SmallPtrSet ProcessedFuncRefs; + // Patterns of symbol names whose references are not expected to be updated + // when changing namespaces around them. + std::vector WhiteListedSymbolRegexes; +}; + +} // namespace change_namespace +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CHANGE_NAMESPACE_CHANGENAMESPACE_H Index: clang-tools-extra/trunk/clang-change-namespace/ChangeNamespace.cpp =================================================================== --- clang-tools-extra/trunk/clang-change-namespace/ChangeNamespace.cpp +++ clang-tools-extra/trunk/clang-change-namespace/ChangeNamespace.cpp @@ -0,0 +1,1042 @@ +//===-- ChangeNamespace.cpp - Change namespace implementation -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "ChangeNamespace.h" +#include "clang/AST/ASTContext.h" +#include "clang/Format/Format.h" +#include "clang/Lex/Lexer.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace change_namespace { + +namespace { + +inline std::string +joinNamespaces(const llvm::SmallVectorImpl &Namespaces) { + if (Namespaces.empty()) + return ""; + std::string Result = Namespaces.front(); + for (auto I = Namespaces.begin() + 1, E = Namespaces.end(); I != E; ++I) + Result += ("::" + *I).str(); + return Result; +} + +// Given "a::b::c", returns {"a", "b", "c"}. +llvm::SmallVector splitSymbolName(llvm::StringRef Name) { + llvm::SmallVector Splitted; + Name.split(Splitted, "::", /*MaxSplit=*/-1, + /*KeepEmpty=*/false); + return Splitted; +} + +SourceLocation startLocationForType(TypeLoc TLoc) { + // For elaborated types (e.g. `struct a::A`) we want the portion after the + // `struct` but including the namespace qualifier, `a::`. + if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) { + NestedNameSpecifierLoc NestedNameSpecifier = + TLoc.castAs().getQualifierLoc(); + if (NestedNameSpecifier.getNestedNameSpecifier()) + return NestedNameSpecifier.getBeginLoc(); + TLoc = TLoc.getNextTypeLoc(); + } + return TLoc.getBeginLoc(); +} + +SourceLocation endLocationForType(TypeLoc TLoc) { + // Dig past any namespace or keyword qualifications. + while (TLoc.getTypeLocClass() == TypeLoc::Elaborated || + TLoc.getTypeLocClass() == TypeLoc::Qualified) + TLoc = TLoc.getNextTypeLoc(); + + // The location for template specializations (e.g. Foo) includes the + // templated types in its location range. We want to restrict this to just + // before the `<` character. + if (TLoc.getTypeLocClass() == TypeLoc::TemplateSpecialization) + return TLoc.castAs() + .getLAngleLoc() + .getLocWithOffset(-1); + return TLoc.getEndLoc(); +} + +// Returns the containing namespace of `InnerNs` by skipping `PartialNsName`. +// If the `InnerNs` does not have `PartialNsName` as suffix, or `PartialNsName` +// is empty, nullptr is returned. +// For example, if `InnerNs` is "a::b::c" and `PartialNsName` is "b::c", then +// the NamespaceDecl of namespace "a" will be returned. +const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs, + llvm::StringRef PartialNsName) { + if (!InnerNs || PartialNsName.empty()) + return nullptr; + const auto *CurrentContext = llvm::cast(InnerNs); + const auto *CurrentNs = InnerNs; + auto PartialNsNameSplitted = splitSymbolName(PartialNsName); + while (!PartialNsNameSplitted.empty()) { + // Get the inner-most namespace in CurrentContext. + while (CurrentContext && !llvm::isa(CurrentContext)) + CurrentContext = CurrentContext->getParent(); + if (!CurrentContext) + return nullptr; + CurrentNs = llvm::cast(CurrentContext); + if (PartialNsNameSplitted.back() != CurrentNs->getNameAsString()) + return nullptr; + PartialNsNameSplitted.pop_back(); + CurrentContext = CurrentContext->getParent(); + } + return CurrentNs; +} + +static std::unique_ptr +getLexerStartingFromLoc(SourceLocation Loc, const SourceManager &SM, + const LangOptions &LangOpts) { + if (Loc.isMacroID() && + !Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc)) + return nullptr; + // Break down the source location. + std::pair LocInfo = SM.getDecomposedLoc(Loc); + // Try to load the file buffer. + bool InvalidTemp = false; + llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp); + if (InvalidTemp) + return nullptr; + + const char *TokBegin = File.data() + LocInfo.second; + // Lex from the start of the given location. + return llvm::make_unique(SM.getLocForStartOfFile(LocInfo.first), + LangOpts, File.begin(), TokBegin, File.end()); +} + +// FIXME: get rid of this helper function if this is supported in clang-refactor +// library. +static SourceLocation getStartOfNextLine(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts) { + std::unique_ptr Lex = getLexerStartingFromLoc(Loc, SM, LangOpts); + if (!Lex.get()) + return SourceLocation(); + llvm::SmallVector Line; + // FIXME: this is a bit hacky to get ReadToEndOfLine work. + Lex->setParsingPreprocessorDirective(true); + Lex->ReadToEndOfLine(&Line); + auto End = Loc.getLocWithOffset(Line.size()); + return SM.getLocForEndOfFile(SM.getDecomposedLoc(Loc).first) == End + ? End + : End.getLocWithOffset(1); +} + +// Returns `R` with new range that refers to code after `Replaces` being +// applied. +tooling::Replacement +getReplacementInChangedCode(const tooling::Replacements &Replaces, + const tooling::Replacement &R) { + unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset()); + unsigned NewEnd = + Replaces.getShiftedCodePosition(R.getOffset() + R.getLength()); + return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart, + R.getReplacementText()); +} + +// Adds a replacement `R` into `Replaces` or merges it into `Replaces` by +// applying all existing Replaces first if there is conflict. +void addOrMergeReplacement(const tooling::Replacement &R, + tooling::Replacements *Replaces) { + auto Err = Replaces->add(R); + if (Err) { + llvm::consumeError(std::move(Err)); + auto Replace = getReplacementInChangedCode(*Replaces, R); + *Replaces = Replaces->merge(tooling::Replacements(Replace)); + } +} + +tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End, + llvm::StringRef ReplacementText, + const SourceManager &SM) { + if (!Start.isValid() || !End.isValid()) { + llvm::errs() << "start or end location were invalid\n"; + return tooling::Replacement(); + } + if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) { + llvm::errs() + << "start or end location were in different macro expansions\n"; + return tooling::Replacement(); + } + Start = SM.getSpellingLoc(Start); + End = SM.getSpellingLoc(End); + if (SM.getFileID(Start) != SM.getFileID(End)) { + llvm::errs() << "start or end location were in different files\n"; + return tooling::Replacement(); + } + return tooling::Replacement( + SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start), + SM.getSpellingLoc(End)), + ReplacementText); +} + +void addReplacementOrDie( + SourceLocation Start, SourceLocation End, llvm::StringRef ReplacementText, + const SourceManager &SM, + std::map *FileToReplacements) { + const auto R = createReplacement(Start, End, ReplacementText, SM); + auto Err = (*FileToReplacements)[R.getFilePath()].add(R); + if (Err) + llvm_unreachable(llvm::toString(std::move(Err)).c_str()); +} + +tooling::Replacement createInsertion(SourceLocation Loc, + llvm::StringRef InsertText, + const SourceManager &SM) { + if (Loc.isInvalid()) { + llvm::errs() << "insert Location is invalid.\n"; + return tooling::Replacement(); + } + Loc = SM.getSpellingLoc(Loc); + return tooling::Replacement(SM, Loc, 0, InsertText); +} + +// Returns the shortest qualified name for declaration `DeclName` in the +// namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName` +// is "a::c::d", then "b::X" will be returned. +// Note that if `DeclName` is `::b::X` and `NsName` is `::a::b`, this returns +// "::b::X" instead of "b::X" since there will be a name conflict otherwise. +// \param DeclName A fully qualified name, "::a::b::X" or "a::b::X". +// \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace +// will have empty name. +std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName, + llvm::StringRef NsName) { + DeclName = DeclName.ltrim(':'); + NsName = NsName.ltrim(':'); + if (DeclName.find(':') == llvm::StringRef::npos) + return DeclName; + + auto NsNameSplitted = splitSymbolName(NsName); + auto DeclNsSplitted = splitSymbolName(DeclName); + llvm::StringRef UnqualifiedDeclName = DeclNsSplitted.pop_back_val(); + // If the Decl is in global namespace, there is no need to shorten it. + if (DeclNsSplitted.empty()) + return UnqualifiedDeclName; + // If NsName is the global namespace, we can simply use the DeclName sans + // leading "::". + if (NsNameSplitted.empty()) + return DeclName; + + if (NsNameSplitted.front() != DeclNsSplitted.front()) { + // The DeclName must be fully-qualified, but we still need to decide if a + // leading "::" is necessary. For example, if `NsName` is "a::b::c" and the + // `DeclName` is "b::X", then the reference must be qualified as "::b::X" + // to avoid conflict. + if (llvm::is_contained(NsNameSplitted, DeclNsSplitted.front())) + return ("::" + DeclName).str(); + return DeclName; + } + // Since there is already an overlap namespace, we know that `DeclName` can be + // shortened, so we reduce the longest common prefix. + auto DeclI = DeclNsSplitted.begin(); + auto DeclE = DeclNsSplitted.end(); + auto NsI = NsNameSplitted.begin(); + auto NsE = NsNameSplitted.end(); + for (; DeclI != DeclE && NsI != NsE && *DeclI == *NsI; ++DeclI, ++NsI) { + } + return (DeclI == DeclE) + ? UnqualifiedDeclName.str() + : (llvm::join(DeclI, DeclE, "::") + "::" + UnqualifiedDeclName) + .str(); +} + +std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) { + if (Code.back() != '\n') + Code += "\n"; + auto NsSplitted = splitSymbolName(NestedNs); + while (!NsSplitted.empty()) { + // FIXME: consider code style for comments. + Code = ("namespace " + NsSplitted.back() + " {\n" + Code + + "} // namespace " + NsSplitted.back() + "\n") + .str(); + NsSplitted.pop_back(); + } + return Code; +} + +// Returns true if \p D is a nested DeclContext in \p Context +bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) { + while (D) { + if (D == Context) + return true; + D = D->getParent(); + } + return false; +} + +// Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx. +bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D, + const DeclContext *DeclCtx, SourceLocation Loc) { + SourceLocation DeclLoc = SM.getSpellingLoc(D->getBeginLoc()); + Loc = SM.getSpellingLoc(Loc); + return SM.isBeforeInTranslationUnit(DeclLoc, Loc) && + (SM.getFileID(DeclLoc) == SM.getFileID(Loc) && + isNestedDeclContext(DeclCtx, D->getDeclContext())); +} + +// Given a qualified symbol name, returns true if the symbol will be +// incorrectly qualified without leading "::". For example, a symbol +// "nx::ny::Foo" in namespace "na::nx::ny" without leading "::"; a symbol +// "util::X" in namespace "na" can potentially conflict with "na::util" (if this +// exists). +bool conflictInNamespace(const ASTContext &AST, llvm::StringRef QualifiedSymbol, + llvm::StringRef Namespace) { + auto SymbolSplitted = splitSymbolName(QualifiedSymbol.trim(":")); + assert(!SymbolSplitted.empty()); + SymbolSplitted.pop_back(); // We are only interested in namespaces. + + if (SymbolSplitted.size() >= 1 && !Namespace.empty()) { + auto SymbolTopNs = SymbolSplitted.front(); + auto NsSplitted = splitSymbolName(Namespace.trim(":")); + assert(!NsSplitted.empty()); + + auto LookupDecl = [&AST](const Decl &Scope, + llvm::StringRef Name) -> const NamedDecl * { + const auto *DC = llvm::dyn_cast(&Scope); + if (!DC) + return nullptr; + auto LookupRes = DC->lookup(DeclarationName(&AST.Idents.get(Name))); + if (LookupRes.empty()) + return nullptr; + return LookupRes.front(); + }; + // We do not check the outermost namespace since it would not be a + // conflict if it equals to the symbol's outermost namespace and the + // symbol name would have been shortened. + const NamedDecl *Scope = + LookupDecl(*AST.getTranslationUnitDecl(), NsSplitted.front()); + for (auto I = NsSplitted.begin() + 1, E = NsSplitted.end(); I != E; ++I) { + if (*I == SymbolTopNs) // Handles "::ny" in "::nx::ny" case. + return true; + // Handles "::util" and "::nx::util" conflicts. + if (Scope) { + if (LookupDecl(*Scope, SymbolTopNs)) + return true; + Scope = LookupDecl(*Scope, *I); + } + } + if (Scope && LookupDecl(*Scope, SymbolTopNs)) + return true; + } + return false; +} + +AST_MATCHER(EnumDecl, isScoped) { + return Node.isScoped(); +} + +bool isTemplateParameter(TypeLoc Type) { + while (!Type.isNull()) { + if (Type.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm) + return true; + Type = Type.getNextTypeLoc(); + } + return false; +} + +} // anonymous namespace + +ChangeNamespaceTool::ChangeNamespaceTool( + llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern, + llvm::ArrayRef WhiteListedSymbolPatterns, + std::map *FileToReplacements, + llvm::StringRef FallbackStyle) + : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements), + OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')), + FilePattern(FilePattern), FilePatternRE(FilePattern) { + FileToReplacements->clear(); + auto OldNsSplitted = splitSymbolName(OldNamespace); + auto NewNsSplitted = splitSymbolName(NewNamespace); + // Calculates `DiffOldNamespace` and `DiffNewNamespace`. + while (!OldNsSplitted.empty() && !NewNsSplitted.empty() && + OldNsSplitted.front() == NewNsSplitted.front()) { + OldNsSplitted.erase(OldNsSplitted.begin()); + NewNsSplitted.erase(NewNsSplitted.begin()); + } + DiffOldNamespace = joinNamespaces(OldNsSplitted); + DiffNewNamespace = joinNamespaces(NewNsSplitted); + + for (const auto &Pattern : WhiteListedSymbolPatterns) + WhiteListedSymbolRegexes.emplace_back(Pattern); +} + +void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) { + std::string FullOldNs = "::" + OldNamespace; + // Prefix is the outer-most namespace in DiffOldNamespace. For example, if the + // OldNamespace is "a::b::c" and DiffOldNamespace is "b::c", then Prefix will + // be "a::b". Declarations in this namespace will not be visible in the new + // namespace. If DiffOldNamespace is empty, Prefix will be a invalid name "-". + llvm::SmallVector DiffOldNsSplitted; + llvm::StringRef(DiffOldNamespace) + .split(DiffOldNsSplitted, "::", /*MaxSplit=*/-1, + /*KeepEmpty=*/false); + std::string Prefix = "-"; + if (!DiffOldNsSplitted.empty()) + Prefix = (StringRef(FullOldNs).drop_back(DiffOldNamespace.size()) + + DiffOldNsSplitted.front()) + .str(); + auto IsInMovedNs = + allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")), + isExpansionInFileMatching(FilePattern)); + auto IsVisibleInNewNs = anyOf( + IsInMovedNs, unless(hasAncestor(namespaceDecl(hasName(Prefix))))); + // Match using declarations. + Finder->addMatcher( + usingDecl(isExpansionInFileMatching(FilePattern), IsVisibleInNewNs) + .bind("using"), + this); + // Match using namespace declarations. + Finder->addMatcher(usingDirectiveDecl(isExpansionInFileMatching(FilePattern), + IsVisibleInNewNs) + .bind("using_namespace"), + this); + // Match namespace alias declarations. + Finder->addMatcher(namespaceAliasDecl(isExpansionInFileMatching(FilePattern), + IsVisibleInNewNs) + .bind("namespace_alias"), + this); + + // Match old namespace blocks. + Finder->addMatcher( + namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern)) + .bind("old_ns"), + this); + + // Match class forward-declarations in the old namespace. + // Note that forward-declarations in classes are not matched. + Finder->addMatcher(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())), + IsInMovedNs, hasParent(namespaceDecl())) + .bind("class_fwd_decl"), + this); + + // Match template class forward-declarations in the old namespace. + Finder->addMatcher( + classTemplateDecl(unless(hasDescendant(cxxRecordDecl(isDefinition()))), + IsInMovedNs, hasParent(namespaceDecl())) + .bind("template_class_fwd_decl"), + this); + + // Match references to types that are not defined in the old namespace. + // Forward-declarations in the old namespace are also matched since they will + // be moved back to the old namespace. + auto DeclMatcher = namedDecl( + hasAncestor(namespaceDecl()), + unless(anyOf( + isImplicit(), hasAncestor(namespaceDecl(isAnonymous())), + hasAncestor(cxxRecordDecl()), + allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition()))))))); + + // Using shadow declarations in classes always refers to base class, which + // does not need to be qualified since it can be inferred from inheritance. + // Note that this does not match using alias declarations. + auto UsingShadowDeclInClass = + usingDecl(hasAnyUsingShadowDecl(decl()), hasParent(cxxRecordDecl())); + + // Match TypeLocs on the declaration. Carefully match only the outermost + // TypeLoc and template specialization arguments (which are not outermost) + // that are directly linked to types matching `DeclMatcher`. Nested name + // specifier locs are handled separately below. + Finder->addMatcher( + typeLoc(IsInMovedNs, + loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))), + unless(anyOf(hasParent(typeLoc(loc(qualType( + hasDeclaration(DeclMatcher), + unless(templateSpecializationType()))))), + hasParent(nestedNameSpecifierLoc()), + hasAncestor(isImplicit()), + hasAncestor(UsingShadowDeclInClass), + hasAncestor(functionDecl(isDefaulted())))), + hasAncestor(decl().bind("dc"))) + .bind("type"), + this); + + // Types in `UsingShadowDecl` is not matched by `typeLoc` above, so we need to + // special case it. + // Since using declarations inside classes must have the base class in the + // nested name specifier, we leave it to the nested name specifier matcher. + Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()), + unless(UsingShadowDeclInClass)) + .bind("using_with_shadow"), + this); + + // Handle types in nested name specifier. Specifiers that are in a TypeLoc + // matched above are not matched, e.g. "A::" in "A::A" is not matched since + // "A::A" would have already been fixed. + Finder->addMatcher( + nestedNameSpecifierLoc( + hasAncestor(decl(IsInMovedNs).bind("dc")), + loc(nestedNameSpecifier( + specifiesType(hasDeclaration(DeclMatcher.bind("from_decl"))))), + unless(anyOf(hasAncestor(isImplicit()), + hasAncestor(UsingShadowDeclInClass), + hasAncestor(functionDecl(isDefaulted())), + hasAncestor(typeLoc(loc(qualType(hasDeclaration( + decl(equalsBoundNode("from_decl")))))))))) + .bind("nested_specifier_loc"), + this); + + // Matches base class initializers in constructors. TypeLocs of base class + // initializers do not need to be fixed. For example, + // class X : public a::b::Y { + // public: + // X() : Y::Y() {} // Y::Y do not need namespace specifier. + // }; + Finder->addMatcher( + cxxCtorInitializer(isBaseInitializer()).bind("base_initializer"), this); + + // Handle function. + // Only handle functions that are defined in a namespace excluding member + // function, static methods (qualified by nested specifier), and functions + // defined in the global namespace. + // Note that the matcher does not exclude calls to out-of-line static method + // definitions, so we need to exclude them in the callback handler. + auto FuncMatcher = + functionDecl(unless(anyOf(cxxMethodDecl(), IsInMovedNs, + hasAncestor(namespaceDecl(isAnonymous())), + hasAncestor(cxxRecordDecl()))), + hasParent(namespaceDecl())); + Finder->addMatcher(expr(hasAncestor(decl().bind("dc")), IsInMovedNs, + unless(hasAncestor(isImplicit())), + anyOf(callExpr(callee(FuncMatcher)).bind("call"), + declRefExpr(to(FuncMatcher.bind("func_decl"))) + .bind("func_ref"))), + this); + + auto GlobalVarMatcher = varDecl( + hasGlobalStorage(), hasParent(namespaceDecl()), + unless(anyOf(IsInMovedNs, hasAncestor(namespaceDecl(isAnonymous()))))); + Finder->addMatcher(declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")), + to(GlobalVarMatcher.bind("var_decl"))) + .bind("var_ref"), + this); + + // Handle unscoped enum constant. + auto UnscopedEnumMatcher = enumConstantDecl(hasParent(enumDecl( + hasParent(namespaceDecl()), + unless(anyOf(isScoped(), IsInMovedNs, hasAncestor(cxxRecordDecl()), + hasAncestor(namespaceDecl(isAnonymous()))))))); + Finder->addMatcher( + declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")), + to(UnscopedEnumMatcher.bind("enum_const_decl"))) + .bind("enum_const_ref"), + this); +} + +void ChangeNamespaceTool::run( + const ast_matchers::MatchFinder::MatchResult &Result) { + if (const auto *Using = Result.Nodes.getNodeAs("using")) { + UsingDecls.insert(Using); + } else if (const auto *UsingNamespace = + Result.Nodes.getNodeAs( + "using_namespace")) { + UsingNamespaceDecls.insert(UsingNamespace); + } else if (const auto *NamespaceAlias = + Result.Nodes.getNodeAs( + "namespace_alias")) { + NamespaceAliasDecls.insert(NamespaceAlias); + } else if (const auto *NsDecl = + Result.Nodes.getNodeAs("old_ns")) { + moveOldNamespace(Result, NsDecl); + } else if (const auto *FwdDecl = + Result.Nodes.getNodeAs("class_fwd_decl")) { + moveClassForwardDeclaration(Result, cast(FwdDecl)); + } else if (const auto *TemplateFwdDecl = + Result.Nodes.getNodeAs( + "template_class_fwd_decl")) { + moveClassForwardDeclaration(Result, cast(TemplateFwdDecl)); + } else if (const auto *UsingWithShadow = + Result.Nodes.getNodeAs("using_with_shadow")) { + fixUsingShadowDecl(Result, UsingWithShadow); + } else if (const auto *Specifier = + Result.Nodes.getNodeAs( + "nested_specifier_loc")) { + SourceLocation Start = Specifier->getBeginLoc(); + SourceLocation End = endLocationForType(Specifier->getTypeLoc()); + fixTypeLoc(Result, Start, End, Specifier->getTypeLoc()); + } else if (const auto *BaseInitializer = + Result.Nodes.getNodeAs( + "base_initializer")) { + BaseCtorInitializerTypeLocs.push_back( + BaseInitializer->getTypeSourceInfo()->getTypeLoc()); + } else if (const auto *TLoc = Result.Nodes.getNodeAs("type")) { + // This avoids fixing types with record types as qualifier, which is not + // filtered by matchers in some cases, e.g. the type is templated. We should + // handle the record type qualifier instead. + TypeLoc Loc = *TLoc; + while (Loc.getTypeLocClass() == TypeLoc::Qualified) + Loc = Loc.getNextTypeLoc(); + if (Loc.getTypeLocClass() == TypeLoc::Elaborated) { + NestedNameSpecifierLoc NestedNameSpecifier = + Loc.castAs().getQualifierLoc(); + // This happens for friend declaration of a base class with injected class + // name. + if (!NestedNameSpecifier.getNestedNameSpecifier()) + return; + const Type *SpecifierType = + NestedNameSpecifier.getNestedNameSpecifier()->getAsType(); + if (SpecifierType && SpecifierType->isRecordType()) + return; + } + fixTypeLoc(Result, startLocationForType(Loc), endLocationForType(Loc), Loc); + } else if (const auto *VarRef = + Result.Nodes.getNodeAs("var_ref")) { + const auto *Var = Result.Nodes.getNodeAs("var_decl"); + assert(Var); + if (Var->getCanonicalDecl()->isStaticDataMember()) + return; + const auto *Context = Result.Nodes.getNodeAs("dc"); + assert(Context && "Empty decl context."); + fixDeclRefExpr(Result, Context->getDeclContext(), + llvm::cast(Var), VarRef); + } else if (const auto *EnumConstRef = + Result.Nodes.getNodeAs("enum_const_ref")) { + // Do not rename the reference if it is already scoped by the EnumDecl name. + if (EnumConstRef->hasQualifier() && + EnumConstRef->getQualifier()->getKind() == + NestedNameSpecifier::SpecifierKind::TypeSpec && + EnumConstRef->getQualifier()->getAsType()->isEnumeralType()) + return; + const auto *EnumConstDecl = + Result.Nodes.getNodeAs("enum_const_decl"); + assert(EnumConstDecl); + const auto *Context = Result.Nodes.getNodeAs("dc"); + assert(Context && "Empty decl context."); + // FIXME: this would qualify "ns::VALUE" as "ns::EnumValue::VALUE". Fix it + // if it turns out to be an issue. + fixDeclRefExpr(Result, Context->getDeclContext(), + llvm::cast(EnumConstDecl), EnumConstRef); + } else if (const auto *FuncRef = + Result.Nodes.getNodeAs("func_ref")) { + // If this reference has been processed as a function call, we do not + // process it again. + if (ProcessedFuncRefs.count(FuncRef)) + return; + ProcessedFuncRefs.insert(FuncRef); + const auto *Func = Result.Nodes.getNodeAs("func_decl"); + assert(Func); + const auto *Context = Result.Nodes.getNodeAs("dc"); + assert(Context && "Empty decl context."); + fixDeclRefExpr(Result, Context->getDeclContext(), + llvm::cast(Func), FuncRef); + } else { + const auto *Call = Result.Nodes.getNodeAs("call"); + assert(Call != nullptr && "Expecting callback for CallExpr."); + const auto *CalleeFuncRef = + llvm::cast(Call->getCallee()->IgnoreImplicit()); + ProcessedFuncRefs.insert(CalleeFuncRef); + const FunctionDecl *Func = Call->getDirectCallee(); + assert(Func != nullptr); + // FIXME: ignore overloaded operators. This would miss cases where operators + // are called by qualified names (i.e. "ns::operator <"). Ignore such + // cases for now. + if (Func->isOverloadedOperator()) + return; + // Ignore out-of-line static methods since they will be handled by nested + // name specifiers. + if (Func->getCanonicalDecl()->getStorageClass() == + StorageClass::SC_Static && + Func->isOutOfLine()) + return; + const auto *Context = Result.Nodes.getNodeAs("dc"); + assert(Context && "Empty decl context."); + SourceRange CalleeRange = Call->getCallee()->getSourceRange(); + replaceQualifiedSymbolInDeclContext( + Result, Context->getDeclContext(), CalleeRange.getBegin(), + CalleeRange.getEnd(), llvm::cast(Func)); + } +} + +static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl, + const SourceManager &SM, + const LangOptions &LangOpts) { + std::unique_ptr Lex = + getLexerStartingFromLoc(NsDecl->getBeginLoc(), SM, LangOpts); + assert(Lex.get() && + "Failed to create lexer from the beginning of namespace."); + if (!Lex.get()) + return SourceLocation(); + Token Tok; + while (!Lex->LexFromRawLexer(Tok) && Tok.isNot(tok::TokenKind::l_brace)) { + } + return Tok.isNot(tok::TokenKind::l_brace) + ? SourceLocation() + : Tok.getEndLoc().getLocWithOffset(1); +} + +// Stores information about a moved namespace in `MoveNamespaces` and leaves +// the actual movement to `onEndOfTranslationUnit()`. +void ChangeNamespaceTool::moveOldNamespace( + const ast_matchers::MatchFinder::MatchResult &Result, + const NamespaceDecl *NsDecl) { + // If the namespace is empty, do nothing. + if (Decl::castToDeclContext(NsDecl)->decls_empty()) + return; + + const SourceManager &SM = *Result.SourceManager; + // Get the range of the code in the old namespace. + SourceLocation Start = + getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()); + assert(Start.isValid() && "Can't find l_brace for namespace."); + MoveNamespace MoveNs; + MoveNs.Offset = SM.getFileOffset(Start); + // The range of the moved namespace is from the location just past the left + // brace to the location right before the right brace. + MoveNs.Length = SM.getFileOffset(NsDecl->getRBraceLoc()) - MoveNs.Offset; + + // Insert the new namespace after `DiffOldNamespace`. For example, if + // `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then + // "x::y" will be inserted inside the existing namespace "a" and after "a::b". + // `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b" + // in the above example. + // If there is no outer namespace (i.e. DiffOldNamespace is empty), the new + // namespace will be a nested namespace in the old namespace. + const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace); + SourceLocation InsertionLoc = Start; + if (OuterNs) { + SourceLocation LocAfterNs = getStartOfNextLine( + OuterNs->getRBraceLoc(), SM, Result.Context->getLangOpts()); + assert(LocAfterNs.isValid() && + "Failed to get location after DiffOldNamespace"); + InsertionLoc = LocAfterNs; + } + MoveNs.InsertionOffset = SM.getFileOffset(SM.getSpellingLoc(InsertionLoc)); + MoveNs.FID = SM.getFileID(Start); + MoveNs.SourceMgr = Result.SourceManager; + MoveNamespaces[SM.getFilename(Start)].push_back(MoveNs); +} + +// Removes a class forward declaration from the code in the moved namespace and +// creates an `InsertForwardDeclaration` to insert the forward declaration back +// into the old namespace after moving code from the old namespace to the new +// namespace. +// For example, changing "a" to "x": +// Old code: +// namespace a { +// class FWD; +// class A { FWD *fwd; } +// } // a +// New code: +// namespace a { +// class FWD; +// } // a +// namespace x { +// class A { a::FWD *fwd; } +// } // x +void ChangeNamespaceTool::moveClassForwardDeclaration( + const ast_matchers::MatchFinder::MatchResult &Result, + const NamedDecl *FwdDecl) { + SourceLocation Start = FwdDecl->getBeginLoc(); + SourceLocation End = FwdDecl->getEndLoc(); + const SourceManager &SM = *Result.SourceManager; + SourceLocation AfterSemi = Lexer::findLocationAfterToken( + End, tok::semi, SM, Result.Context->getLangOpts(), + /*SkipTrailingWhitespaceAndNewLine=*/true); + if (AfterSemi.isValid()) + End = AfterSemi.getLocWithOffset(-1); + // Delete the forward declaration from the code to be moved. + addReplacementOrDie(Start, End, "", SM, &FileToReplacements); + llvm::StringRef Code = Lexer::getSourceText( + CharSourceRange::getTokenRange(SM.getSpellingLoc(Start), + SM.getSpellingLoc(End)), + SM, Result.Context->getLangOpts()); + // Insert the forward declaration back into the old namespace after moving the + // code from old namespace to new namespace. + // Insertion information is stored in `InsertFwdDecls` and actual + // insertion will be performed in `onEndOfTranslationUnit`. + // Get the (old) namespace that contains the forward declaration. + const auto *NsDecl = Result.Nodes.getNodeAs("ns_decl"); + // The namespace contains the forward declaration, so it must not be empty. + assert(!NsDecl->decls_empty()); + const auto Insertion = createInsertion( + getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()), + Code, SM); + InsertForwardDeclaration InsertFwd; + InsertFwd.InsertionOffset = Insertion.getOffset(); + InsertFwd.ForwardDeclText = Insertion.getReplacementText().str(); + InsertFwdDecls[Insertion.getFilePath()].push_back(InsertFwd); +} + +// Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p +// FromDecl with the shortest qualified name possible when the reference is in +// `NewNamespace`. +void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext( + const ast_matchers::MatchFinder::MatchResult &Result, + const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End, + const NamedDecl *FromDecl) { + const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext(); + if (llvm::isa(NsDeclContext)) { + // This should not happen in usual unless the TypeLoc is in function type + // parameters, e.g `std::function`. In this case, DeclContext of + // `T` will be the translation unit. We simply use fully-qualified name + // here. + // Note that `FromDecl` must not be defined in the old namespace (according + // to `DeclMatcher`), so its fully-qualified name will not change after + // changing the namespace. + addReplacementOrDie(Start, End, FromDecl->getQualifiedNameAsString(), + *Result.SourceManager, &FileToReplacements); + return; + } + const auto *NsDecl = llvm::cast(NsDeclContext); + // Calculate the name of the `NsDecl` after it is moved to new namespace. + std::string OldNs = NsDecl->getQualifiedNameAsString(); + llvm::StringRef Postfix = OldNs; + bool Consumed = Postfix.consume_front(OldNamespace); + assert(Consumed && "Expect OldNS to start with OldNamespace."); + (void)Consumed; + const std::string NewNs = (NewNamespace + Postfix).str(); + + llvm::StringRef NestedName = Lexer::getSourceText( + CharSourceRange::getTokenRange( + Result.SourceManager->getSpellingLoc(Start), + Result.SourceManager->getSpellingLoc(End)), + *Result.SourceManager, Result.Context->getLangOpts()); + std::string FromDeclName = FromDecl->getQualifiedNameAsString(); + for (llvm::Regex &RE : WhiteListedSymbolRegexes) + if (RE.match(FromDeclName)) + return; + std::string ReplaceName = + getShortestQualifiedNameInNamespace(FromDeclName, NewNs); + // Checks if there is any using namespace declarations that can shorten the + // qualified name. + for (const auto *UsingNamespace : UsingNamespaceDecls) { + if (!isDeclVisibleAtLocation(*Result.SourceManager, UsingNamespace, DeclCtx, + Start)) + continue; + StringRef FromDeclNameRef = FromDeclName; + if (FromDeclNameRef.consume_front(UsingNamespace->getNominatedNamespace() + ->getQualifiedNameAsString())) { + FromDeclNameRef = FromDeclNameRef.drop_front(2); + if (FromDeclNameRef.size() < ReplaceName.size()) + ReplaceName = FromDeclNameRef; + } + } + // Checks if there is any namespace alias declarations that can shorten the + // qualified name. + for (const auto *NamespaceAlias : NamespaceAliasDecls) { + if (!isDeclVisibleAtLocation(*Result.SourceManager, NamespaceAlias, DeclCtx, + Start)) + continue; + StringRef FromDeclNameRef = FromDeclName; + if (FromDeclNameRef.consume_front( + NamespaceAlias->getNamespace()->getQualifiedNameAsString() + + "::")) { + std::string AliasName = NamespaceAlias->getNameAsString(); + std::string AliasQualifiedName = + NamespaceAlias->getQualifiedNameAsString(); + // We only consider namespace aliases define in the global namepspace or + // in namespaces that are directly visible from the reference, i.e. + // ancestor of the `OldNs`. Note that declarations in ancestor namespaces + // but not visible in the new namespace is filtered out by + // "IsVisibleInNewNs" matcher. + if (AliasQualifiedName != AliasName) { + // The alias is defined in some namespace. + assert(StringRef(AliasQualifiedName).endswith("::" + AliasName)); + llvm::StringRef AliasNs = + StringRef(AliasQualifiedName).drop_back(AliasName.size() + 2); + if (!llvm::StringRef(OldNs).startswith(AliasNs)) + continue; + } + std::string NameWithAliasNamespace = + (AliasName + "::" + FromDeclNameRef).str(); + if (NameWithAliasNamespace.size() < ReplaceName.size()) + ReplaceName = NameWithAliasNamespace; + } + } + // Checks if there is any using shadow declarations that can shorten the + // qualified name. + bool Matched = false; + for (const UsingDecl *Using : UsingDecls) { + if (Matched) + break; + if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) { + for (const auto *UsingShadow : Using->shadows()) { + const auto *TargetDecl = UsingShadow->getTargetDecl(); + if (TargetDecl->getQualifiedNameAsString() == + FromDecl->getQualifiedNameAsString()) { + ReplaceName = FromDecl->getNameAsString(); + Matched = true; + break; + } + } + } + } + bool Conflict = conflictInNamespace(DeclCtx->getParentASTContext(), + ReplaceName, NewNamespace); + // If the new nested name in the new namespace is the same as it was in the + // old namespace, we don't create replacement unless there can be ambiguity. + if ((NestedName == ReplaceName && !Conflict) || + (NestedName.startswith("::") && NestedName.drop_front(2) == ReplaceName)) + return; + // If the reference need to be fully-qualified, add a leading "::" unless + // NewNamespace is the global namespace. + if (ReplaceName == FromDeclName && !NewNamespace.empty() && Conflict) + ReplaceName = "::" + ReplaceName; + addReplacementOrDie(Start, End, ReplaceName, *Result.SourceManager, + &FileToReplacements); +} + +// Replace the [Start, End] of `Type` with the shortest qualified name when the +// `Type` is in `NewNamespace`. +void ChangeNamespaceTool::fixTypeLoc( + const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start, + SourceLocation End, TypeLoc Type) { + // FIXME: do not rename template parameter. + if (Start.isInvalid() || End.isInvalid()) + return; + // Types of CXXCtorInitializers do not need to be fixed. + if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type)) + return; + if (isTemplateParameter(Type)) + return; + // The declaration which this TypeLoc refers to. + const auto *FromDecl = Result.Nodes.getNodeAs("from_decl"); + // `hasDeclaration` gives underlying declaration, but if the type is + // a typedef type, we need to use the typedef type instead. + auto IsInMovedNs = [&](const NamedDecl *D) { + if (!llvm::StringRef(D->getQualifiedNameAsString()) + .startswith(OldNamespace + "::")) + return false; + auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getBeginLoc()); + if (ExpansionLoc.isInvalid()) + return false; + llvm::StringRef Filename = Result.SourceManager->getFilename(ExpansionLoc); + return FilePatternRE.match(Filename); + }; + // Make `FromDecl` the immediate declaration that `Type` refers to, i.e. if + // `Type` is an alias type, we make `FromDecl` the type alias declaration. + // Also, don't fix the \p Type if it refers to a type alias decl in the moved + // namespace since the alias decl will be moved along with the type reference. + if (auto *Typedef = Type.getType()->getAs()) { + FromDecl = Typedef->getDecl(); + if (IsInMovedNs(FromDecl)) + return; + } else if (auto *TemplateType = + Type.getType()->getAs()) { + if (TemplateType->isTypeAlias()) { + FromDecl = TemplateType->getTemplateName().getAsTemplateDecl(); + if (IsInMovedNs(FromDecl)) + return; + } + } + const auto *DeclCtx = Result.Nodes.getNodeAs("dc"); + assert(DeclCtx && "Empty decl context."); + replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start, + End, FromDecl); +} + +void ChangeNamespaceTool::fixUsingShadowDecl( + const ast_matchers::MatchFinder::MatchResult &Result, + const UsingDecl *UsingDeclaration) { + SourceLocation Start = UsingDeclaration->getBeginLoc(); + SourceLocation End = UsingDeclaration->getEndLoc(); + if (Start.isInvalid() || End.isInvalid()) + return; + + assert(UsingDeclaration->shadow_size() > 0); + // FIXME: it might not be always accurate to use the first using-decl. + const NamedDecl *TargetDecl = + UsingDeclaration->shadow_begin()->getTargetDecl(); + std::string TargetDeclName = TargetDecl->getQualifiedNameAsString(); + // FIXME: check if target_decl_name is in moved ns, which doesn't make much + // sense. If this happens, we need to use name with the new namespace. + // Use fully qualified name in UsingDecl for now. + addReplacementOrDie(Start, End, "using ::" + TargetDeclName, + *Result.SourceManager, &FileToReplacements); +} + +void ChangeNamespaceTool::fixDeclRefExpr( + const ast_matchers::MatchFinder::MatchResult &Result, + const DeclContext *UseContext, const NamedDecl *From, + const DeclRefExpr *Ref) { + SourceRange RefRange = Ref->getSourceRange(); + replaceQualifiedSymbolInDeclContext(Result, UseContext, RefRange.getBegin(), + RefRange.getEnd(), From); +} + +void ChangeNamespaceTool::onEndOfTranslationUnit() { + // Move namespace blocks and insert forward declaration to old namespace. + for (const auto &FileAndNsMoves : MoveNamespaces) { + auto &NsMoves = FileAndNsMoves.second; + if (NsMoves.empty()) + continue; + const std::string &FilePath = FileAndNsMoves.first; + auto &Replaces = FileToReplacements[FilePath]; + auto &SM = *NsMoves.begin()->SourceMgr; + llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID); + auto ChangedCode = tooling::applyAllReplacements(Code, Replaces); + if (!ChangedCode) { + llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n"; + continue; + } + // Replacements on the changed code for moving namespaces and inserting + // forward declarations to old namespaces. + tooling::Replacements NewReplacements; + // Cut the changed code from the old namespace and paste the code in the new + // namespace. + for (const auto &NsMove : NsMoves) { + // Calculate the range of the old namespace block in the changed + // code. + const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset); + const unsigned NewLength = + Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) - + NewOffset; + tooling::Replacement Deletion(FilePath, NewOffset, NewLength, ""); + std::string MovedCode = ChangedCode->substr(NewOffset, NewLength); + std::string MovedCodeWrappedInNewNs = + wrapCodeInNamespace(DiffNewNamespace, MovedCode); + // Calculate the new offset at which the code will be inserted in the + // changed code. + unsigned NewInsertionOffset = + Replaces.getShiftedCodePosition(NsMove.InsertionOffset); + tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0, + MovedCodeWrappedInNewNs); + addOrMergeReplacement(Deletion, &NewReplacements); + addOrMergeReplacement(Insertion, &NewReplacements); + } + // After moving namespaces, insert forward declarations back to old + // namespaces. + const auto &FwdDeclInsertions = InsertFwdDecls[FilePath]; + for (const auto &FwdDeclInsertion : FwdDeclInsertions) { + unsigned NewInsertionOffset = + Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset); + tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0, + FwdDeclInsertion.ForwardDeclText); + addOrMergeReplacement(Insertion, &NewReplacements); + } + // Add replacements referring to the changed code to existing replacements, + // which refers to the original code. + Replaces = Replaces.merge(NewReplacements); + auto Style = + format::getStyle(format::DefaultFormatStyle, FilePath, FallbackStyle); + if (!Style) { + llvm::errs() << llvm::toString(Style.takeError()) << "\n"; + continue; + } + // Clean up old namespaces if there is nothing in it after moving. + auto CleanReplacements = + format::cleanupAroundReplacements(Code, Replaces, *Style); + if (!CleanReplacements) { + llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n"; + continue; + } + FileToReplacements[FilePath] = *CleanReplacements; + } + + // Make sure we don't generate replacements for files that do not match + // FilePattern. + for (auto &Entry : FileToReplacements) + if (!FilePatternRE.match(Entry.first)) + Entry.second.clear(); +} + +} // namespace change_namespace +} // namespace clang Index: clang-tools-extra/trunk/clang-change-namespace/tool/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-change-namespace/tool/CMakeLists.txt +++ clang-tools-extra/trunk/clang-change-namespace/tool/CMakeLists.txt @@ -0,0 +1,25 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) + +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_executable(clang-change-namespace + ClangChangeNamespace.cpp + ) +target_link_libraries(clang-change-namespace + PRIVATE + clangAST + clangASTMatchers + clangBasic + clangChangeNamespace + clangFormat + clangFrontend + clangRewrite + clangSerialization + clangTooling + clangToolingCore + ) + +install(TARGETS clang-change-namespace + RUNTIME DESTINATION bin) Index: clang-tools-extra/trunk/clang-change-namespace/tool/ClangChangeNamespace.cpp =================================================================== --- clang-tools-extra/trunk/clang-change-namespace/tool/ClangChangeNamespace.cpp +++ clang-tools-extra/trunk/clang-change-namespace/tool/ClangChangeNamespace.cpp @@ -0,0 +1,177 @@ +//===-- ClangChangeNamespace.cpp - Standalone change namespace ------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This tool can be used to change the surrounding namespaces of class/function +// definitions. +// +// Example: test.cc +// namespace na { +// class X {}; +// namespace nb { +// class Y { X x; }; +// } // namespace nb +// } // namespace na +// To move the definition of class Y from namespace "na::nb" to "x::y", run: +// clang-change-namespace --old_namespace "na::nb" \ +// --new_namespace "x::y" --file_pattern "test.cc" test.cc -- +// Output: +// namespace na { +// class X {}; +// } // namespace na +// namespace x { +// namespace y { +// class Y { na::X x; }; +// } // namespace y +// } // namespace x + +#include "ChangeNamespace.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Refactoring.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/YAMLTraits.h" + +using namespace clang; +using namespace llvm; + +namespace { + +cl::OptionCategory ChangeNamespaceCategory("Change namespace."); + +cl::opt OldNamespace("old_namespace", cl::Required, + cl::desc("Old namespace."), + cl::cat(ChangeNamespaceCategory)); + +cl::opt NewNamespace("new_namespace", cl::Required, + cl::desc("New namespace."), + cl::cat(ChangeNamespaceCategory)); + +cl::opt FilePattern( + "file_pattern", cl::Required, + cl::desc("Only rename namespaces in files that match the given pattern."), + cl::cat(ChangeNamespaceCategory)); + +cl::opt Inplace("i", cl::desc("Inplace edit s, if specified."), + cl::cat(ChangeNamespaceCategory)); + +cl::opt + DumpYAML("dump_result", + cl::desc("Dump new file contents in YAML, if specified."), + cl::cat(ChangeNamespaceCategory)); + +cl::opt Style("style", + cl::desc("The style name used for reformatting."), + cl::init("LLVM"), cl::cat(ChangeNamespaceCategory)); + +cl::opt WhiteListFile( + "whitelist_file", + cl::desc("A file containing regexes of symbol names that are not expected " + "to be updated when changing namespaces around them."), + cl::init(""), cl::cat(ChangeNamespaceCategory)); + +llvm::ErrorOr> GetWhiteListedSymbolPatterns() { + std::vector Patterns; + if (WhiteListFile.empty()) + return Patterns; + + llvm::SmallVector Lines; + llvm::ErrorOr> File = + llvm::MemoryBuffer::getFile(WhiteListFile); + if (!File) + return File.getError(); + llvm::StringRef Content = File.get()->getBuffer(); + Content.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); + for (auto Line : Lines) + Patterns.push_back(Line.trim()); + return Patterns; +} + +} // anonymous namespace + +int main(int argc, const char **argv) { + llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); + tooling::CommonOptionsParser OptionsParser(argc, argv, + ChangeNamespaceCategory); + const auto &Files = OptionsParser.getSourcePathList(); + tooling::RefactoringTool Tool(OptionsParser.getCompilations(), Files); + llvm::ErrorOr> WhiteListPatterns = + GetWhiteListedSymbolPatterns(); + if (!WhiteListPatterns) { + llvm::errs() << "Failed to open whitelist file " << WhiteListFile << ". " + << WhiteListPatterns.getError().message() << "\n"; + return 1; + } + change_namespace::ChangeNamespaceTool NamespaceTool( + OldNamespace, NewNamespace, FilePattern, *WhiteListPatterns, + &Tool.getReplacements(), Style); + ast_matchers::MatchFinder Finder; + NamespaceTool.registerMatchers(&Finder); + std::unique_ptr Factory = + tooling::newFrontendActionFactory(&Finder); + + if (int Result = Tool.run(Factory.get())) + return Result; + LangOptions DefaultLangOptions; + IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); + clang::TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts); + DiagnosticsEngine Diagnostics( + IntrusiveRefCntPtr(new DiagnosticIDs()), &*DiagOpts, + &DiagnosticPrinter, false); + auto &FileMgr = Tool.getFiles(); + SourceManager Sources(Diagnostics, FileMgr); + Rewriter Rewrite(Sources, DefaultLangOptions); + + if (!formatAndApplyAllReplacements(Tool.getReplacements(), Rewrite, Style)) { + llvm::errs() << "Failed applying all replacements.\n"; + return 1; + } + if (Inplace) + return Rewrite.overwriteChangedFiles(); + + std::set ChangedFiles; + for (const auto &it : Tool.getReplacements()) + ChangedFiles.insert(it.first); + + if (DumpYAML) { + auto WriteToYAML = [&](llvm::raw_ostream &OS) { + OS << "[\n"; + for (auto I = ChangedFiles.begin(), E = ChangedFiles.end(); I != E; ++I) { + OS << " {\n"; + OS << " \"FilePath\": \"" << *I << "\",\n"; + const auto *Entry = FileMgr.getFile(*I); + auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User); + std::string Content; + llvm::raw_string_ostream ContentStream(Content); + Rewrite.getEditBuffer(ID).write(ContentStream); + OS << " \"SourceText\": \"" + << llvm::yaml::escape(ContentStream.str()) << "\"\n"; + OS << " }"; + if (I != std::prev(E)) + OS << ",\n"; + } + OS << "\n]\n"; + }; + WriteToYAML(llvm::outs()); + return 0; + } + + for (const auto &File : ChangedFiles) { + const auto *Entry = FileMgr.getFile(File); + + auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User); + outs() << "============== " << File << " ==============\n"; + Rewrite.getEditBuffer(ID).write(llvm::outs()); + outs() << "\n============================================\n"; + } + + return 0; +} Index: clang-tools-extra/trunk/test/change-namespace/Inputs/fake-std.h =================================================================== --- clang-tools-extra/trunk/test/change-namespace/Inputs/fake-std.h +++ clang-tools-extra/trunk/test/change-namespace/Inputs/fake-std.h @@ -1,5 +0,0 @@ -namespace std { - class STD {}; -} - -using namespace std; Index: clang-tools-extra/trunk/test/change-namespace/lambda-function.cpp =================================================================== --- clang-tools-extra/trunk/test/change-namespace/lambda-function.cpp +++ clang-tools-extra/trunk/test/change-namespace/lambda-function.cpp @@ -1,37 +0,0 @@ -// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" %s -- -std=c++11 | sed 's,// CHECK.*,,' | FileCheck %s - -template -class function; -template -class function { -public: - template - function(Functor f) {} - R operator()(ArgTypes...) const {} -}; - -namespace x { -// CHECK: namespace x { -class X {}; -} - -namespace na { -namespace nb { -// CHECK: namespace x { -// CHECK-NEXT: namespace y { -void f(function func, int param) { func(param); } -void g() { f([](int x) {}, 1); } - -// x::X in function type parameter list will have translation unit context, so -// we simply replace it with fully-qualified name. -using TX = function; -// CHECK: using TX = function; - -class A {}; -using TA = function; -// CHECK: using TA = function; - -// CHECK: } // namespace y -// CHECK-NEXT: } // namespace x -} -} Index: clang-tools-extra/trunk/test/change-namespace/macro.cpp =================================================================== --- clang-tools-extra/trunk/test/change-namespace/macro.cpp +++ clang-tools-extra/trunk/test/change-namespace/macro.cpp @@ -1,29 +0,0 @@ -// RUN: cp %S/macro.cpp %T/macro.cpp -// RUN: echo "#define USING using na::nc::X" > %T/macro.h -// -// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern "macro.cpp" --i %T/macro.cpp -- -// RUN: FileCheck -input-file=%T/macro.cpp -check-prefix=CHECK-CC %s -// RUN: FileCheck -input-file=%T/macro.h -check-prefix=CHECK-HEADER %s -// -// RUN: cp %S/macro.cpp %T/macro.cpp -// RUN: echo "#define USING using na::nc::X" > %T/macro.h -// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" --i %T/macro.cpp -- -// RUN: FileCheck -input-file=%T/macro.cpp -check-prefix=CHECK-CC %s -// RUN: FileCheck -input-file=%T/macro.h -check-prefix=CHECK-CHANGED-HEADER %s -#include "macro.h" -namespace na { namespace nc { class X{}; } } - -namespace na { -namespace nb { -USING; -} -} -// CHECK-CC: namespace x { -// CHECK-CC: namespace y { -// CHECK-CC: USING; -// CHECK-CC: } // namespace y -// CHECK-CC: } // namespace x - -// CHECK-HEADER: #define USING using na::nc::X - -// CHECK-CHANGED-HEADER: #define USING using ::na::nc::X Index: clang-tools-extra/trunk/test/change-namespace/simple-move.cpp =================================================================== --- clang-tools-extra/trunk/test/change-namespace/simple-move.cpp +++ clang-tools-extra/trunk/test/change-namespace/simple-move.cpp @@ -1,10 +0,0 @@ -// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" %s -- | sed 's,// CHECK.*,,' | FileCheck %s -// CHECK: namespace x { -// CHECK-NEXT: namespace y { -namespace na { -namespace nb { -class A {}; -// CHECK: } // namespace y -// CHECK-NEXT: } // namespace x -} -} Index: clang-tools-extra/trunk/test/change-namespace/white-list.cpp =================================================================== --- clang-tools-extra/trunk/test/change-namespace/white-list.cpp +++ clang-tools-extra/trunk/test/change-namespace/white-list.cpp @@ -1,19 +0,0 @@ -// RUN: echo "^std::.*$" > %T/white-list.txt -// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" --whitelist_file %T/white-list.txt %s -- | sed 's,// CHECK.*,,' | FileCheck %s - -#include "Inputs/fake-std.h" - -// CHECK: namespace x { -// CHECK-NEXT: namespace y { -namespace na { -namespace nb { -void f() { - std::STD x1; - STD x2; -// CHECK: {{^}} std::STD x1;{{$}} -// CHECK-NEXT: {{^}} STD x2;{{$}} -} -// CHECK: } // namespace y -// CHECK-NEXT: } // namespace x -} -} Index: clang-tools-extra/trunk/test/clang-change-namespace/Inputs/fake-std.h =================================================================== --- clang-tools-extra/trunk/test/clang-change-namespace/Inputs/fake-std.h +++ clang-tools-extra/trunk/test/clang-change-namespace/Inputs/fake-std.h @@ -0,0 +1,5 @@ +namespace std { + class STD {}; +} + +using namespace std; Index: clang-tools-extra/trunk/test/clang-change-namespace/lambda-function.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-change-namespace/lambda-function.cpp +++ clang-tools-extra/trunk/test/clang-change-namespace/lambda-function.cpp @@ -0,0 +1,37 @@ +// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" %s -- -std=c++11 | sed 's,// CHECK.*,,' | FileCheck %s + +template +class function; +template +class function { +public: + template + function(Functor f) {} + R operator()(ArgTypes...) const {} +}; + +namespace x { +// CHECK: namespace x { +class X {}; +} + +namespace na { +namespace nb { +// CHECK: namespace x { +// CHECK-NEXT: namespace y { +void f(function func, int param) { func(param); } +void g() { f([](int x) {}, 1); } + +// x::X in function type parameter list will have translation unit context, so +// we simply replace it with fully-qualified name. +using TX = function; +// CHECK: using TX = function; + +class A {}; +using TA = function; +// CHECK: using TA = function; + +// CHECK: } // namespace y +// CHECK-NEXT: } // namespace x +} +} Index: clang-tools-extra/trunk/test/clang-change-namespace/macro.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-change-namespace/macro.cpp +++ clang-tools-extra/trunk/test/clang-change-namespace/macro.cpp @@ -0,0 +1,29 @@ +// RUN: cp %S/macro.cpp %T/macro.cpp +// RUN: echo "#define USING using na::nc::X" > %T/macro.h +// +// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern "macro.cpp" --i %T/macro.cpp -- +// RUN: FileCheck -input-file=%T/macro.cpp -check-prefix=CHECK-CC %s +// RUN: FileCheck -input-file=%T/macro.h -check-prefix=CHECK-HEADER %s +// +// RUN: cp %S/macro.cpp %T/macro.cpp +// RUN: echo "#define USING using na::nc::X" > %T/macro.h +// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" --i %T/macro.cpp -- +// RUN: FileCheck -input-file=%T/macro.cpp -check-prefix=CHECK-CC %s +// RUN: FileCheck -input-file=%T/macro.h -check-prefix=CHECK-CHANGED-HEADER %s +#include "macro.h" +namespace na { namespace nc { class X{}; } } + +namespace na { +namespace nb { +USING; +} +} +// CHECK-CC: namespace x { +// CHECK-CC: namespace y { +// CHECK-CC: USING; +// CHECK-CC: } // namespace y +// CHECK-CC: } // namespace x + +// CHECK-HEADER: #define USING using na::nc::X + +// CHECK-CHANGED-HEADER: #define USING using ::na::nc::X Index: clang-tools-extra/trunk/test/clang-change-namespace/simple-move.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-change-namespace/simple-move.cpp +++ clang-tools-extra/trunk/test/clang-change-namespace/simple-move.cpp @@ -0,0 +1,10 @@ +// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" %s -- | sed 's,// CHECK.*,,' | FileCheck %s +// CHECK: namespace x { +// CHECK-NEXT: namespace y { +namespace na { +namespace nb { +class A {}; +// CHECK: } // namespace y +// CHECK-NEXT: } // namespace x +} +} Index: clang-tools-extra/trunk/test/clang-change-namespace/white-list.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-change-namespace/white-list.cpp +++ clang-tools-extra/trunk/test/clang-change-namespace/white-list.cpp @@ -0,0 +1,19 @@ +// RUN: echo "^std::.*$" > %T/white-list.txt +// RUN: clang-change-namespace -old_namespace "na::nb" -new_namespace "x::y" --file_pattern ".*" --whitelist_file %T/white-list.txt %s -- | sed 's,// CHECK.*,,' | FileCheck %s + +#include "Inputs/fake-std.h" + +// CHECK: namespace x { +// CHECK-NEXT: namespace y { +namespace na { +namespace nb { +void f() { + std::STD x1; + STD x2; +// CHECK: {{^}} std::STD x1;{{$}} +// CHECK-NEXT: {{^}} STD x2;{{$}} +} +// CHECK: } // namespace y +// CHECK-NEXT: } // namespace x +} +} Index: clang-tools-extra/trunk/unittests/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/unittests/CMakeLists.txt +++ clang-tools-extra/trunk/unittests/CMakeLists.txt @@ -14,8 +14,8 @@ endif() endif() -add_subdirectory(change-namespace) add_subdirectory(clang-apply-replacements) +add_subdirectory(clang-change-namespace) add_subdirectory(clang-doc) add_subdirectory(clang-move) add_subdirectory(clang-query) Index: clang-tools-extra/trunk/unittests/change-namespace/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/unittests/change-namespace/CMakeLists.txt +++ clang-tools-extra/trunk/unittests/change-namespace/CMakeLists.txt @@ -1,30 +0,0 @@ -set(LLVM_LINK_COMPONENTS - support - ) - -get_filename_component(CHANGE_NAMESPACE_SOURCE_DIR - ${CMAKE_CURRENT_SOURCE_DIR}/../../change-namespace REALPATH) -include_directories( - ${CHANGE_NAMESPACE_SOURCE_DIR} - ) - -# We'd like clang/unittests/Tooling/RewriterTestContext.h in the test. -include_directories(${CLANG_SOURCE_DIR}) - -add_extra_unittest(ChangeNamespaceTests - ChangeNamespaceTests.cpp - ) - -target_link_libraries(ChangeNamespaceTests - PRIVATE - clangAST - clangASTMatchers - clangBasic - clangChangeNamespace - clangFormat - clangFrontend - clangRewrite - clangSerialization - clangTooling - clangToolingCore - ) Index: clang-tools-extra/trunk/unittests/change-namespace/ChangeNamespaceTests.cpp =================================================================== --- clang-tools-extra/trunk/unittests/change-namespace/ChangeNamespaceTests.cpp +++ clang-tools-extra/trunk/unittests/change-namespace/ChangeNamespaceTests.cpp @@ -1,2281 +0,0 @@ -//===-- ChangeNamespaceTests.cpp - Change namespace unit tests ---*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "ChangeNamespace.h" -#include "unittests/Tooling/RewriterTestContext.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Basic/FileManager.h" -#include "clang/Basic/FileSystemOptions.h" -#include "clang/Format/Format.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/PCHContainerOperations.h" -#include "clang/Tooling/Refactoring.h" -#include "clang/Tooling/Tooling.h" -#include "llvm/ADT/IntrusiveRefCntPtr.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/VirtualFileSystem.h" -#include "gtest/gtest.h" -#include -#include -#include - -namespace clang { -namespace change_namespace { -namespace { - -class ChangeNamespaceTest : public ::testing::Test { -public: - std::string runChangeNamespaceOnCode(llvm::StringRef Code) { - clang::RewriterTestContext Context; - clang::FileID ID = Context.createInMemoryFile(FileName, Code); - - std::map FileToReplacements; - change_namespace::ChangeNamespaceTool NamespaceTool( - OldNamespace, NewNamespace, FilePattern, - /*WhiteListedSymbolPatterns*/ {}, &FileToReplacements); - ast_matchers::MatchFinder Finder; - NamespaceTool.registerMatchers(&Finder); - std::unique_ptr Factory = - tooling::newFrontendActionFactory(&Finder); - if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, {"-std=c++11"}, - FileName)) - return ""; - formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite); - return format(Context.getRewrittenText(ID)); - } - - std::string format(llvm::StringRef Code) { - tooling::Replacements Replaces = format::reformat( - format::getLLVMStyle(), Code, {tooling::Range(0, Code.size())}); - auto ChangedCode = tooling::applyAllReplacements(Code, Replaces); - EXPECT_TRUE(static_cast(ChangedCode)); - if (!ChangedCode) { - llvm::errs() << llvm::toString(ChangedCode.takeError()); - return ""; - } - return *ChangedCode; - } - -protected: - std::string FileName = "input.cc"; - std::string OldNamespace = "na::nb"; - std::string NewNamespace = "x::y"; - std::string FilePattern = "input.cc"; -}; - -TEST_F(ChangeNamespaceTest, NoMatchingNamespace) { - std::string Code = "namespace na {\n" - "namespace nx {\n" - "class A {};\n" - "} // namespace nx\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "namespace nx {\n" - "class A {};\n" - "} // namespace nx\n" - "} // namespace na\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, SimpleMoveWithoutTypeRefs) { - std::string Code = "namespace na {\n" - "namespace nb {\n" - "class A {};\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "\n\n" - "namespace x {\n" - "namespace y {\n" - "class A {};\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, NewNsNestedInOldNs) { - NewNamespace = "na::nb::nc"; - std::string Code = "namespace na {\n" - "namespace nb {\n" - "class A {};\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "namespace nb {\n" - "namespace nc {\n" - "class A {};\n" - "} // namespace nc\n" - "} // namespace nb\n" - "} // namespace na\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, NewNsNestedInOldNsWithSurroundingNewLines) { - NewNamespace = "na::nb::nc"; - std::string Code = "namespace na {\n" - "namespace nb {\n" - "\n" - "class A {};\n" - "\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "namespace nb {\n" - "namespace nc {\n" - "\n" - "class A {};\n" - "\n" - "} // namespace nc\n" - "} // namespace nb\n" - "} // namespace na\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, MoveOldNsWithSurroundingNewLines) { - NewNamespace = "nx::ny"; - std::string Code = "namespace na {\n" - "namespace nb {\n" - "\n" - "class A {};\n" - "\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "\n\n" - "namespace nx {\n" - "namespace ny {\n" - "\n" - "class A {};\n" - "\n" - "} // namespace ny\n" - "} // namespace nx\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, NewNsNestedInOldNsWithRefs) { - NewNamespace = "na::nb::nc"; - std::string Code = "namespace na {\n" - "class A {};\n" - "namespace nb {\n" - "class B {};\n" - "class C {};\n" - "void f() { A a; B b; }\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "class A {};\n" - "namespace nb {\n" - "namespace nc {\n" - "class B {};\n" - "class C {};\n" - "void f() { A a; B b; }\n" - "} // namespace nc\n" - "} // namespace nb\n" - "} // namespace na\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, SimpleMoveIntoAnotherNestedNamespace) { - NewNamespace = "na::nc"; - std::string Code = "namespace na {\n" - "namespace nb {\n" - "class A {};\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "\n" - "namespace nc {\n" - "class A {};\n" - "} // namespace nc\n" - "} // namespace na\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, MoveIntoAnotherNestedNamespaceWithRef) { - NewNamespace = "na::nc"; - std::string Code = "namespace na {\n" - "class A {};\n" - "namespace nb {\n" - "class X { A a; };\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "class A {};\n" - "\n" - "namespace nc {\n" - "class X { A a; };\n" - "} // namespace nc\n" - "} // namespace na\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, MoveIntoExistingNamespaceAndShortenRefs) { - std::string Code = "namespace x {\n" - "namespace z {\n" - "class Z {};\n" - "} // namespace z\n" - "namespace y {\n" - "class T {};\n" - "} // namespace y\n" - "} // namespace x\n" - "namespace na {\n" - "class A{};\n" - "namespace nb {\n" - "class X { A a; x::z::Z zz; x::y::T t; };\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace x {\n" - "namespace z {\n" - "class Z {};\n" - "} // namespace z\n" - "namespace y {\n" - "class T {};\n" - "} // namespace y\n" - "} // namespace x\n" - "namespace na {\n" - "class A {};\n\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "class X { na::A a; z::Z zz; T t; };\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, SimpleMoveNestedNamespace) { - NewNamespace = "na::x::y"; - std::string Code = "namespace na {\n" - "class A {};\n" - "namespace nb {\n" - "class B {};\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "class A {};\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "class B {};\n" - "} // namespace y\n" - "} // namespace x\n" - "} // namespace na\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, SimpleMoveWithTypeRefs) { - std::string Code = "namespace na {\n" - "class C_A {};\n" - "namespace nc {\n" - "class C_C {};" - "} // namespace nc\n" - "namespace nb {\n" - "class C_X {\n" - "public:\n" - " C_A a;\n" - " nc::C_C c;\n" - "};\n" - "class C_Y {\n" - " C_X x;\n" - "};\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "class C_A {};\n" - "namespace nc {\n" - "class C_C {};" - "} // namespace nc\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "class C_X {\n" - "public:\n" - " na::C_A a;\n" - " na::nc::C_C c;\n" - "};\n" - "class C_Y {\n" - " C_X x;\n" - "};\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, TypeLocInTemplateSpecialization) { - std::string Code = "namespace na {\n" - "class A {};\n" - "template \n" - "class B {};\n" - "template \n" - "class Two {};\n" - "namespace nc { class C {}; }\n" - "} // na\n" - "\n" - "namespace na {\n" - "namespace nb {\n" - "void f() {\n" - " B b;\n" - " B b_c;\n" - " Two two;\n" - "}\n" - "} // nb\n" - "} // na\n"; - std::string Expected = "namespace na {\n" - "class A {};\n" - "template \n" - "class B {};\n" - "template \n" - "class Two {};\n" - "namespace nc { class C {}; }\n" - "} // na\n" - "\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "void f() {\n" - " na::B b;\n" - " na::B b_c;\n" - " na::Two two;\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, LeaveForwardDeclarationBehind) { - std::string Code = "namespace na {\n" - "namespace nb {\n" - "class FWD;\n" - "class FWD2;\n" - "class A {\n" - " FWD *fwd;\n" - "};\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "namespace nb {\n" - "class FWD;\n" - "class FWD2;\n" - "} // namespace nb\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "\n" - "class A {\n" - " na::nb::FWD *fwd;\n" - "};\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, InsertForwardDeclsProperly) { - std::string Code = "namespace na {\n" - "namespace nb {\n" - "\n" - "class FWD;\n" - "class FWD2;\n" - "class A {\n" - " FWD *fwd;\n" - "};\n" - "\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "namespace nb {\n" - "class FWD;\n" - "class FWD2;\n" - "} // namespace nb\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "\n" - "class A {\n" - " na::nb::FWD *fwd;\n" - "};\n" - "\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, TemplateClassForwardDeclaration) { - std::string Code = "namespace na {\n" - "namespace nb {\n" - "class FWD;\n" - "template class FWD_TEMP;\n" - "class A {\n" - " FWD *fwd;\n" - "};\n" - "template class TEMP {};\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "namespace nb {\n" - "class FWD;\n" - "template class FWD_TEMP;\n" - "} // namespace nb\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "\n" - "class A {\n" - " na::nb::FWD *fwd;\n" - "};\n" - "template class TEMP {};\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, DontMoveForwardDeclarationInClass) { - std::string Code = "namespace na {\n" - "namespace nb {\n" - "class A {\n" - " class FWD;\n" - " FWD *fwd;\n" - " template class FWD_TEMP;\n" - "};\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "\n\n" - "namespace x {\n" - "namespace y {\n" - "class A {\n" - " class FWD;\n" - " FWD *fwd;\n" - " template class FWD_TEMP;\n" - "};\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, MoveFunctions) { - std::string Code = "namespace na {\n" - "class C_A {};\n" - "namespace nc {\n" - "class C_C {};" - "} // namespace nc\n" - "namespace nb {\n" - "void fwd();\n" - "void f(C_A ca, nc::C_C cc) {\n" - " C_A ca_1 = ca;\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace na {\n" - "class C_A {};\n" - "namespace nc {\n" - "class C_C {};" - "} // namespace nc\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "void fwd();\n" - "void f(na::C_A ca, na::nc::C_C cc) {\n" - " na::C_A ca_1 = ca;\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, FixUsingShadowDecl) { - std::string Code = "class GLOB {};\n" - "using BLOG = GLOB;\n" - "namespace na {\n" - "namespace nc {\n" - "class SAME {};\n" - "}\n" - "namespace nd {\n" - "class SAME {};\n" - "}\n" - "namespace nb {\n" - "using nc::SAME;\n" - "using YO = nd::SAME;\n" - "typedef nd::SAME IDENTICAL;\n" - "void f(nd::SAME Same) {}\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "class GLOB {};\n" - "using BLOG = GLOB;\n" - "namespace na {\n" - "namespace nc {\n" - "class SAME {};\n" - "}\n" - "namespace nd {\n" - "class SAME {};\n" - "}\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "using ::na::nc::SAME;\n" - "using YO = na::nd::SAME;\n" - "typedef na::nd::SAME IDENTICAL;\n" - "void f(na::nd::SAME Same) {}\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, DontFixUsingShadowDeclInClasses) { - std::string Code = "namespace na {\n" - "class A {};\n" - "class Base { public: Base() {} void m() {} };\n" - "namespace nb {\n" - "class D : public Base {\n" - "public:\n" - " using AA = A; using B = Base;\n" - " using Base::m; using Base::Base;\n" - "};" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace na {\n" - "class A {};\n" - "class Base { public: Base() {} void m() {} };\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "class D : public na::Base {\n" - "public:\n" - " using AA = na::A; using B = na::Base;\n" - " using Base::m; using Base::Base;\n" - "};" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, TypeInNestedNameSpecifier) { - std::string Code = - "namespace na {\n" - "class C_A {\n" - "public:\n" - " class Nested {\n" - " public:\n" - " static int NestedX;\n" - " static void nestedFunc() {}\n" - " };\n" - "};\n" - "namespace nb {\n" - "class C_X {\n" - " C_A na;\n" - " C_A::Nested nested;\n" - " void f() {\n" - " C_A::Nested::nestedFunc();\n" - " int X = C_A::Nested::NestedX;\n" - " }\n" - "};\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = - "namespace na {\n" - "class C_A {\n" - "public:\n" - " class Nested {\n" - " public:\n" - " static int NestedX;\n" - " static void nestedFunc() {}\n" - " };\n" - "};\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "class C_X {\n" - " na::C_A na;\n" - " na::C_A::Nested nested;\n" - " void f() {\n" - " na::C_A::Nested::nestedFunc();\n" - " int X = na::C_A::Nested::NestedX;\n" - " }\n" - "};\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, FixFunctionNameSpecifiers) { - std::string Code = - "namespace na {\n" - "class A {\n" - "public:\n" - " static void f() {}\n" - " static void g();\n" - "};\n" - "void A::g() {}" - "void a_f() {}\n" - "static void static_f() {}\n" - "namespace nb {\n" - "void f() { a_f(); static_f(); A::f(); }\n" - "void g() { f(); A::g(); }\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = - "namespace na {\n" - "class A {\n" - "public:\n" - " static void f() {}\n" - " static void g();\n" - "};\n" - "void A::g() {}" - "void a_f() {}\n" - "static void static_f() {}\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "void f() { na::a_f(); na::static_f(); na::A::f(); }\n" - "void g() { f(); na::A::g(); }\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, FixOverloadedOperatorFunctionNameSpecifiers) { - std::string Code = - "namespace na {\n" - "class A {\n" - "public:\n" - " int x;\n" - " bool operator==(const A &RHS) const { return x == RHS.x; }\n" - "};\n" - "bool operator<(const A &LHS, const A &RHS) { return LHS.x == RHS.x; }\n" - "namespace nb {\n" - "bool f() {\n" - " A x, y;\n" - " auto f = operator<;\n" - " return (x == y) && (x < y) && (operator<(x, y));\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = - "namespace na {\n" - "class A {\n" - "public:\n" - " int x;\n" - " bool operator==(const A &RHS) const { return x == RHS.x; }\n" - "};\n" - "bool operator<(const A &LHS, const A &RHS) { return LHS.x == RHS.x; }\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "bool f() {\n" - " na::A x, y;\n" - " auto f = na::operator<;\n" - // FIXME: function calls to overloaded operators are not fixed now even if - // they are referenced by qualified names. - " return (x == y) && (x < y) && (operator<(x,y));\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, FixNonCallingFunctionReferences) { - std::string Code = "namespace na {\n" - "class A {\n" - "public:\n" - " static void f() {}\n" - "};\n" - "void a_f() {}\n" - "static void s_f() {}\n" - "namespace nb {\n" - "void f() {\n" - " auto *ref1 = A::f;\n" - " auto *ref2 = a_f;\n" - " auto *ref3 = s_f;\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = - "namespace na {\n" - "class A {\n" - "public:\n" - " static void f() {}\n" - "};\n" - "void a_f() {}\n" - "static void s_f() {}\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "void f() {\n" - " auto *ref1 = na::A::f;\n" - " auto *ref2 = na::a_f;\n" - " auto *ref3 = na::s_f;\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, MoveAndFixGlobalVariables) { - std::string Code = "namespace na {\n" - "int GlobA;\n" - "static int GlobAStatic = 0;\n" - "namespace nc { int GlobC; }\n" - "namespace nb {\n" - "int GlobB;\n" - "void f() {\n" - " int a = GlobA;\n" - " int b = GlobAStatic;\n" - " int c = nc::GlobC;\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace na {\n" - "int GlobA;\n" - "static int GlobAStatic = 0;\n" - "namespace nc { int GlobC; }\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "int GlobB;\n" - "void f() {\n" - " int a = na::GlobA;\n" - " int b = na::GlobAStatic;\n" - " int c = na::nc::GlobC;\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, DoNotFixStaticVariableOfClass) { - std::string Code = "namespace na {\n" - "class A {\n" - "public:\n" - "static int A1;\n" - "static int A2;\n" - "};\n" - "int A::A1 = 0;\n" - "namespace nb {\n" - "void f() {\n" - " int a = A::A1; int b = A::A2;\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace na {\n" - "class A {\n" - "public:\n" - "static int A1;\n" - "static int A2;\n" - "};\n" - "int A::A1 = 0;\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "void f() {\n" - " int a = na::A::A1; int b = na::A::A2;\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, NoMisplaceAtEOF) { - std::string Code = "namespace na {\n" - "namespace nb {\n" - "class A;\n" - "class B {};\n" - "}" - "}"; - std::string Expected = "namespace na {\n" - "namespace nb {\n" - "class A;\n" - "}\n" - "}\n" - "namespace x {\n" - "namespace y {\n" - "\n" - "class B {};\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, CommentsBeforeMovedClass) { - std::string Code = "namespace na {\n" - "namespace nb {\n" - "\n\n" - "// Wild comments.\n" - "\n" - "// Comments.\n" - "// More comments.\n" - "class B {\n" - " // Private comments.\n" - " int a;\n" - "};\n" - "}\n" - "}"; - std::string Expected = "\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "\n\n" - "// Wild comments.\n" - "\n" - "// Comments.\n" - "// More comments.\n" - "class B {\n" - " // Private comments.\n" - " int a;\n" - "};\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingShadowDeclInGlobal) { - std::string Code = "namespace glob {\n" - "class Glob {};\n" - "void GFunc() {}\n" - "}\n" - "using glob::Glob;\n" - "using glob::GFunc;\n" - "namespace na {\n" - "namespace nb {\n" - "void f() { Glob g; GFunc(); }\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace glob {\n" - "class Glob {};\n" - "void GFunc() {}\n" - "}\n" - "using glob::Glob;\n" - "using glob::GFunc;\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "void f() { Glob g; GFunc(); }\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingShadowDeclsInAnonymousNamespaces) { - std::string Code = "namespace util {\n" - "class Util {};\n" - "void func() {}\n" - "}\n" - "namespace na {\n" - "namespace nb {\n" - "namespace {\n" - "using ::util::Util;\n" - "using ::util::func;\n" - "void f() { Util u; func(); }\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace util {\n" - "class Util {};\n" - "void func() {}\n" - "} // namespace util\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "namespace {\n" - "using ::util::Util;\n" - "using ::util::func;\n" - "void f() { Util u; func(); }\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingNamespaceInGlobal) { - std::string Code = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "using namespace glob;\n" - "namespace na {\n" - "namespace nb {\n" - "void f() { Glob g; }\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "using namespace glob;\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "void f() { Glob g; }\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, NamespaceAliasInGlobal) { - std::string Code = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "namespace glob2 { class Glob2 {}; }\n" - "namespace gl = glob;\n" - "namespace gl2 = glob2;\n" - "namespace na {\n" - "namespace nb {\n" - "void f() { gl::Glob g; gl2::Glob2 g2; }\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = - "namespace glob {\n" - "class Glob {};\n" - "}\n" - "namespace glob2 { class Glob2 {}; }\n" - "namespace gl = glob;\n" - "namespace gl2 = glob2;\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "void f() { gl::Glob g; gl2::Glob2 g2; }\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, NamespaceAliasInNamespace) { - std::string Code = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "namespace na {\n" - "namespace nb {\n" - "namespace gl = glob;\n" - "void f() { gl::Glob g; }\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "namespace gl = glob;\n" - "void f() { gl::Glob g; }\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, NamespaceAliasInAncestorNamespace) { - NewNamespace = "na::nx"; - std::string Code = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "namespace other { namespace gl = glob; }\n" - "namespace na {\n" - "namespace ga = glob;\n" - "namespace nb {\n" - "void f() { ga::Glob g; }\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "namespace other { namespace gl = glob; }\n" - "namespace na {\n" - "namespace ga = glob;\n" - "\n" - "namespace nx {\n" - "void f() { ga::Glob g; }\n" - "} // namespace nx\n" - "} // namespace na\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, NamespaceAliasInOtherNamespace) { - std::string Code = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "namespace other { namespace gl = glob; }\n" - "namespace na {\n" - "namespace ga = glob;\n" - "namespace nb {\n" - "void f() { glob::Glob g; }\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "namespace other { namespace gl = glob; }\n" - "namespace na {\n" - "namespace ga = glob;\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "void f() { glob::Glob g; }\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingDeclAfterReference) { - std::string Code = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "namespace na {\n" - "namespace nb {\n" - "void f() { glob::Glob g; }\n" - "} // namespace nb\n" - "} // namespace na\n" - "using glob::Glob;\n" - "using namespace glob;\n"; - - std::string Expected = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "void f() { glob::Glob g; }\n" - "} // namespace y\n" - "} // namespace x\n" - "using glob::Glob;\n" - "using namespace glob;\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingNamespaceAfterReference) { - NewNamespace = "na::nc"; - std::string Code = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "namespace na {\n" - "namespace nb {\n" - "void f() { glob::Glob g; }\n" - "} // namespace nb\n" - "using namespace glob;\n" - "} // namespace na\n"; - - std::string Expected = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "namespace na {\n" - "\n" - "namespace nc {\n" - "void f() { glob::Glob g; }\n" - "} // namespace nc\n" - "using namespace glob;\n" - "} // namespace na\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingNamespaceAndUsingShadowInGlobal) { - std::string Code = "namespace glob1 {\n" - "namespace glob2 {\n" - "class Glob {};\n" - "}\n" - "}\n" - "using glob1::glob2::Glob;\n" - "using namespace glob1;\n" - "namespace na {\n" - "namespace nb {\n" - "void f() { Glob g; }\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace glob1 {\n" - "namespace glob2 {\n" - "class Glob {};\n" - "}\n" - "}\n" - "using glob1::glob2::Glob;\n" - "using namespace glob1;\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "void f() { Glob g; }\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingAliasInGlobal) { - std::string Code = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "using GLB = glob::Glob;\n" - "using BLG = glob::Glob;\n" - "namespace na {\n" - "namespace nb {\n" - "void f() { GLB g; BLG blg; }\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "using GLB = glob::Glob;\n" - "using BLG = glob::Glob;\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "void f() { GLB g; BLG blg; }\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingShadowDeclAndMovedNamespace) { - std::string Code = "namespace na { class C_A {};\n }\n" - "using na::C_A;\n" - "namespace na {\n" - "namespace nb {\n" - "class C_X {\n" - "public:\n" - " C_A a;\n" - "};\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na { class C_A {};\n }\n" - "using na::C_A;\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "class C_X {\n" - "public:\n" - " C_A a;\n" - "};\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingNamespaceDeclAndMovedNamespace) { - std::string Code = "namespace na { class C_A {};\n }\n" - "using namespace na;\n" - "namespace na {\n" - "namespace nb {\n" - "class C_X {\n" - "public:\n" - " C_A ca;\n" - "};\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na { class C_A {};\n }\n" - "using namespace na;\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "class C_X {\n" - "public:\n" - " C_A ca;\n" - "};\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingShadowDeclInFunction) { - std::string Code = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "namespace na {\n" - "namespace nb {\n" - "void f() {\n" - " using glob::Glob;\n" - " Glob g;\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace glob {\n" - "class Glob {};\n" - "}\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "void f() {\n" - " using ::glob::Glob;\n" - " Glob g;\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingShadowDeclInClass) { - std::string Code = "namespace na { class C_A {}; }\n" - "namespace na {\n" - "namespace nb {\n" - "void f() {\n" - " using ::na::C_A;\n" - " C_A ca;\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na { class C_A {}; }\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "void f() {\n" - " using ::na::C_A;\n" - " C_A ca;\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingDeclInMovedNamespace) { - std::string Code = "namespace nx { void f(); }\n" - "namespace na {\n" - "using nx::f;\n" - "namespace nb {\n" - "void d() { f(); }\n" - "} // nb\n" - "} // na\n"; - - std::string Expected = "namespace nx { void f(); }\n" - "namespace na {\n" - "using nx::f;\n" - "\n" - "} // na\n" - "namespace x {\n" - "namespace y {\n" - "void d() { nx::f(); }\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingDeclInMovedNamespaceNotNested) { - OldNamespace = "na"; - std::string Code = "namespace nx { void f(); }\n" - "namespace na {\n" - "using ::nx::f;\n" - "void d() { f(); }\n" - "} // na\n"; - - std::string Expected = "namespace nx { void f(); }\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "using ::nx::f;\n" - "void d() { f(); }\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingDeclInMovedNamespaceMultiNested) { - OldNamespace = "a::b::c::d"; - NewNamespace = "a::b::x::y"; - std::string Code = "namespace nx { void f(); void g(); }\n" - "namespace a {\n" - "namespace b {\n" - "using ::nx::f;\n" - "namespace c {\n" - "using ::nx::g;\n" - "namespace d {\n" - "void d() { f(); g(); }\n" - "} // d\n" - "} // c\n" - "} // b\n" - "} // a\n"; - - std::string Expected = "namespace nx { void f(); void g(); }\n" - "namespace a {\n" - "namespace b {\n" - "using ::nx::f;\n" - "namespace c {\n" - "using ::nx::g;\n" - "\n" - "} // c\n" - "namespace x {\n" - "namespace y {\n" - "void d() { f(); nx::g(); }\n" - "} // namespace y\n" - "} // namespace x\n" - "} // b\n" - "} // a\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingShadowDeclInTheParentOfOldNamespace) { - OldNamespace = "nb::nc"; - NewNamespace = "nb::nd"; - std::string Code = "namespace na { class A {}; }\n" - "namespace nb {\n" - "using na::A;\n" - "namespace nc {\n" - "void d() { A a; }\n" - "} // nc\n" - "} // nb\n"; - - std::string Expected = "namespace na { class A {}; }\n" - "namespace nb {\n" - "using na::A;\n" - "\n" - "namespace nd {\n" - "void d() { A a; }\n" - "} // namespace nd\n" - "} // nb\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingShadowDeclInOldNamespace) { - OldNamespace = "nb"; - NewNamespace = "nc"; - std::string Code = "namespace na { class A {}; }\n" - "namespace nb {\n" - "using na::A;\n" - "void d() { A a; }\n" - "struct X { A a; };\n" - "} // nb\n"; - - std::string Expected = "namespace na { class A {}; }\n" - "\n" - "namespace nc {\n" - "using ::na::A;\n" - "void d() { A a; }\n" - "struct X { A a; };\n" - "} // namespace nc\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingShadowDeclOfTemplateClass) { - OldNamespace = "nb"; - NewNamespace = "nc"; - std::string Code = "namespace na {\n" - "template \n" - "class A { T t; };\n" - "} // namespace na\n" - "namespace nb {\n" - "using na::A;\n" - "void d() { A a; }\n" - "} // nb\n"; - - std::string Expected = "namespace na {\n" - "template \n" - "class A { T t; };\n" - "} // namespace na\n" - "\n" - "namespace nc {\n" - "using ::na::A;\n" - "void d() { A a; }\n" - "} // namespace nc\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingShadowDeclOfTemplateFunction) { - OldNamespace = "nb"; - NewNamespace = "nc"; - std::string Code = "namespace na {\n" - "template \n" - "void f() { T t; };\n" - "} // namespace na\n" - "namespace nb {\n" - "using na::f;\n" - "void d() { f(); }\n" - "} // nb\n"; - - std::string Expected = "namespace na {\n" - "template \n" - "void f() { T t; };\n" - "} // namespace na\n" - "\n" - "namespace nc {\n" - "using ::na::f;\n" - "void d() { f(); }\n" - "} // namespace nc\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingAliasDecl) { - std::string Code = - "namespace nx { namespace ny { class X {}; } }\n" - "namespace na {\n" - "namespace nb {\n" - "using Y = nx::ny::X;\n" - "void f() { Y y; }\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace nx { namespace ny { class X {}; } }\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "using Y = nx::ny::X;\n" - "void f() { Y y; }\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingAliasDeclInGlobal) { - std::string Code = - "namespace nx { namespace ny { class X {}; } }\n" - "using Y = nx::ny::X;\n" - "namespace na {\n" - "namespace nb {\n" - "void f() { Y y; }\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace nx { namespace ny { class X {}; } }\n" - "using Y = nx::ny::X;\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "void f() { Y y; }\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - - -TEST_F(ChangeNamespaceTest, TypedefAliasDecl) { - std::string Code = - "namespace nx { namespace ny { class X {}; } }\n" - "namespace na {\n" - "namespace nb {\n" - "typedef nx::ny::X Y;\n" - "void f() { Y y; }\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = "namespace nx { namespace ny { class X {}; } }\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "typedef nx::ny::X Y;\n" - "void f() { Y y; }\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, DerivedClassWithConstructors) { - std::string Code = - "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n" - "namespace na {\n" - "namespace nb {\n" - "class A : public nx::ny::X {\n" - "public:\n" - " A() : X(0) {}\n" - " A(int i);\n" - "};\n" - "A::A(int i) : X(i) {}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = - "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n" - "\n\n" - "namespace x {\n" - "namespace y {\n" - "class A : public nx::ny::X {\n" - "public:\n" - " A() : X(0) {}\n" - " A(int i);\n" - "};\n" - "A::A(int i) : X(i) {}\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, DerivedClassWithQualifiedConstructors) { - std::string Code = - "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n" - "namespace na {\n" - "namespace nb {\n" - "class A : public nx::ny::X {\n" - "public:\n" - " A() : X::X(0) {}\n" - " A(int i);\n" - "};\n" - "A::A(int i) : X::X(i) {}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = - "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n" - "\n\n" - "namespace x {\n" - "namespace y {\n" - "class A : public nx::ny::X {\n" - "public:\n" - " A() : X::X(0) {}\n" - " A(int i);\n" - "};\n" - "A::A(int i) : X::X(i) {}\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, DerivedClassWithConstructorsAndTypeRefs) { - std::string Code = - "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n" - "namespace na {\n" - "namespace nb {\n" - "class A : public nx::ny::X {\n" - "public:\n" - " A() : X(0) {}\n" - " A(int i);\n" - "};\n" - "A::A(int i) : X(i) { X x(1);}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = - "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n" - "\n\n" - "namespace x {\n" - "namespace y {\n" - "class A : public nx::ny::X {\n" - "public:\n" - " A() : X(0) {}\n" - " A(int i);\n" - "};\n" - "A::A(int i) : X(i) { nx::ny::X x(1);}\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, MoveToGlobalNamespace) { - NewNamespace = ""; - std::string Code = "namespace na {\n" - "class C_A {};\n" - "namespace nc {\n" - "class C_C {};" - "} // namespace nc\n" - "namespace nb {\n" - "class C_X {\n" - "public:\n" - " C_A a;\n" - " nc::C_C c;\n" - "};\n" - "class C_Y {\n" - " C_X x;\n" - "};\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "class C_A {};\n" - "namespace nc {\n" - "class C_C {};" - "} // namespace nc\n" - "\n" - "} // namespace na\n" - "class C_X {\n" - "public:\n" - " na::C_A a;\n" - " na::nc::C_C c;\n" - "};\n" - "class C_Y {\n" - " C_X x;\n" - "};\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, KeepGlobalSpecifier) { - std::string Code = "class Glob {};\n" - "namespace na {\n" - "class C_A {};\n" - "namespace nc {\n" - "class C_C {};" - "} // namespace nc\n" - "namespace nb {\n" - "class C_X {\n" - "public:\n" - " ::Glob glob_1;\n" - " Glob glob_2;\n" - " C_A a_1;\n" - " ::na::C_A a_2;\n" - " nc::C_C c;\n" - "};\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "class Glob {};\n" - "namespace na {\n" - "class C_A {};\n" - "namespace nc {\n" - "class C_C {};" - "} // namespace nc\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "class C_X {\n" - "public:\n" - " ::Glob glob_1;\n" - " Glob glob_2;\n" - " na::C_A a_1;\n" - " ::na::C_A a_2;\n" - " na::nc::C_C c;\n" - "};\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, UsingAliasInTemplate) { - NewNamespace = "na::nb::nc"; - std::string Code = "namespace some_ns {\n" - "template \n" - "class G {};\n" - "} // namespace some_ns\n" - "namespace na {\n" - "template\n" - "using GG = some_ns::G;\n" - "} // namespace na\n" - "namespace na {\n" - "namespace nb {\n" - "void f() {\n" - " GG g;\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace some_ns {\n" - "template \n" - "class G {};\n" - "} // namespace some_ns\n" - "namespace na {\n" - "template\n" - "using GG = some_ns::G;\n" - "} // namespace na\n" - "namespace na {\n" - "namespace nb {\n" - "namespace nc {\n" - "void f() {\n" - " GG g;\n" - "}\n" - "} // namespace nc\n" - "} // namespace nb\n" - "} // namespace na\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, TemplateUsingAliasInBaseClass) { - NewNamespace = "na::nb::nc"; - std::string Code = "namespace some_ns {\n" - "template \n" - "class G {};\n" - "} // namespace some_ns\n" - "namespace na {\n" - "class Base {\n" - "public:\n" - " template\n" - " using GG = some_ns::G;\n" - "\n" - " struct Nested {};\n" - "};\n" - "class Derived : public Base {};\n" - "} // namespace na\n" - "namespace na {\n" - "namespace nb {\n" - "void f() {\n" - " Derived::GG g;\n" - " const Derived::GG gg;\n" - " const Derived::GG* gg_ptr;\n" - " struct Derived::Nested nested;\n" - " const struct Derived::Nested *nested_ptr;\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace some_ns {\n" - "template \n" - "class G {};\n" - "} // namespace some_ns\n" - "namespace na {\n" - "class Base {\n" - "public:\n" - " template\n" - " using GG = some_ns::G;\n" - "\n" - " struct Nested {};\n" - "};\n" - "class Derived : public Base {};\n" - "} // namespace na\n" - "namespace na {\n" - "namespace nb {\n" - "namespace nc {\n" - "void f() {\n" - " Derived::GG g;\n" - " const Derived::GG gg;\n" - " const Derived::GG* gg_ptr;\n" - " struct Derived::Nested nested;\n" - " const struct Derived::Nested *nested_ptr;\n" - "}\n" - "} // namespace nc\n" - "} // namespace nb\n" - "} // namespace na\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, ExistingNamespaceConflictWithNewNamespace) { - OldNamespace = "nx"; - NewNamespace = "ny::na::nc"; - std::string Code = "namespace na {\n" - "class A {};\n" - "} // namespace na\n" - "namespace nb {\n" - "class B {};\n" - "} // namespace nb\n" - "namespace nx {\n" - "class X {\n" - " na::A a; nb::B b;\n" - "};\n" - "} // namespace nx\n"; - std::string Expected = "namespace na {\n" - "class A {};\n" - "} // namespace na\n" - "namespace nb {\n" - "class B {};\n" - "} // namespace nb\n" - "\n" - "namespace ny {\n" - "namespace na {\n" - "namespace nc {\n" - "class X {\n" - " ::na::A a; nb::B b;\n" - "};\n" - "} // namespace nc\n" - "} // namespace na\n" - "} // namespace ny\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, SymbolConflictWithNewNamespace) { - OldNamespace = "nx"; - NewNamespace = "ny::na::nc"; - std::string Code = "namespace na {\n" - "class A {};\n" - "namespace nb {\n" - "class B {};\n" - "} // namespace nb\n" - "} // namespace na\n" - "namespace ny {\n" - "class Y {};\n" - "}\n" - "namespace nx {\n" - "class X {\n" - " na::A a; na::nb::B b;\n" - " ny::Y y;" - "};\n" - "} // namespace nx\n"; - std::string Expected = "namespace na {\n" - "class A {};\n" - "namespace nb {\n" - "class B {};\n" - "} // namespace nb\n" - "} // namespace na\n" - "namespace ny {\n" - "class Y {};\n" - "}\n" - "\n" - "namespace ny {\n" - "namespace na {\n" - "namespace nc {\n" - "class X {\n" - " ::na::A a; ::na::nb::B b;\n" - " Y y;\n" - "};\n" - "} // namespace nc\n" - "} // namespace na\n" - "} // namespace ny\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, ShortenNamespaceSpecifier) { - OldNamespace = "nx"; - NewNamespace = "ny::na"; - std::string Code = "class G {};\n" - "namespace ny {\n" - "class Y {};\n" - "namespace na {\n" - "class A {};\n" - "namespace nc { class C {}; } // namespace nc\n" - "}\n // namespace na\n" - "}\n // namespace ny\n" - "namespace nx {\n" - "class X {\n" - " G g; ny::Y y; ny::na::A a; ny::na::nc::C c;\n" - "};\n" - "} // namespace nx\n"; - std::string Expected = "class G {};\n" - "namespace ny {\n" - "class Y {};\n" - "namespace na {\n" - "class A {};\n" - "namespace nc { class C {}; } // namespace nc\n" - "}\n // namespace na\n" - "}\n // namespace ny\n" - "\n" - "namespace ny {\n" - "namespace na {\n" - "class X {\n" - " G g; Y y; A a; nc::C c;\n" - "};\n" - "} // namespace na\n" - "} // namespace ny\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, ShortenNamespaceSpecifierInAnonymousNamespace) { - OldNamespace = "nx"; - NewNamespace = "ny::na"; - std::string Code = "class G {};\n" - "namespace ny {\n" - "class Y {};\n" - "namespace na {\n" - "class A {};\n" - "namespace nc { class C {}; } // namespace nc\n" - "}\n // namespace na\n" - "}\n // namespace ny\n" - "namespace nx {\n" - "namespace {\n" - "class X {\n" - " G g; ::ny::Y y; ::ny::na::A a; ::ny::na::nc::C c;\n" - "};\n" - "} // namespace\n" - "} // namespace nx\n"; - std::string Expected = "class G {};\n" - "namespace ny {\n" - "class Y {};\n" - "namespace na {\n" - "class A {};\n" - "namespace nc { class C {}; } // namespace nc\n" - "}\n // namespace na\n" - "}\n // namespace ny\n" - "\n" - "namespace ny {\n" - "namespace na {\n" - "namespace {\n" - "class X {\n" - " G g; Y y; A a; nc::C c;\n" - "};\n" - "} // namespace\n" - "} // namespace na\n" - "} // namespace ny\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, SimpleMoveEnum) { - std::string Code = "namespace na {\n" - "namespace nb {\n" - "enum class X { X1, X2 };\n" - "enum Y { Y1, Y2 };\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "\n\nnamespace x {\n" - "namespace y {\n" - "enum class X { X1, X2 };\n" - "enum Y { Y1, Y2 };\n" - "} // namespace y\n" - "} // namespace x\n"; - - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, ReferencesToEnums) { - std::string Code = "enum Glob { G1, G2 };\n" - "namespace na {\n" - "enum class X { X1 };\n" - "enum Y { Y1, Y2 };\n" - "namespace nb {\n" - "void f() {\n" - " Glob g1 = Glob::G1;\n" - " Glob g2 = G2;\n" - " X x1 = X::X1;\n" - " Y y1 = Y::Y1;\n" - " Y y2 = Y2;\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "enum Glob { G1, G2 };\n" - "namespace na {\n" - "enum class X { X1 };\n" - "enum Y { Y1, Y2 };\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "void f() {\n" - " Glob g1 = Glob::G1;\n" - " Glob g2 = G2;\n" - " na::X x1 = na::X::X1;\n" - " na::Y y1 = na::Y::Y1;\n" - " na::Y y2 = na::Y2;\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, NoRedundantEnumUpdate) { - std::string Code = "namespace ns {\n" - "enum class X { X1 };\n" - "enum Y { Y1, Y2 };\n" - "} // namespace ns\n" - "namespace na {\n" - "namespace nb {\n" - "void f() {\n" - " ns::X x1 = ns::X::X1;\n" - " ns::Y y1 = ns::Y::Y1;\n" - " ns::Y y2 = ns::Y2;\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace ns {\n" - "enum class X { X1 };\n" - "enum Y { Y1, Y2 };\n" - "} // namespace ns\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "void f() {\n" - " ns::X x1 = ns::X::X1;\n" - " ns::Y y1 = ns::Y::Y1;\n" - " ns::Y y2 = ns::Y2;\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - ; - - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, EnumsAndUsingShadows) { - std::string Code = "namespace ns {\n" - "enum class X { X1 };\n" - "enum Y { Y1, Y2, Y3 };\n" - "} // namespace ns\n" - "using ns::X;\n" - "using ns::Y;\n" - "using ns::Y::Y2;\n" - "using ns::Y::Y3;\n" - "namespace na {\n" - "namespace nb {\n" - "void f() {\n" - " X x1 = X::X1;\n" - " Y y1 = Y::Y1;\n" - " Y y2 = Y2;\n" - " Y y3 = Y3;\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace ns {\n" - "enum class X { X1 };\n" - "enum Y { Y1, Y2, Y3 };\n" - "} // namespace ns\n" - "using ns::X;\n" - "using ns::Y;\n" - "using ns::Y::Y2;\n" - "using ns::Y::Y3;\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "void f() {\n" - " X x1 = X::X1;\n" - " Y y1 = Y::Y1;\n" - " Y y2 = Y2;\n" - " Y y3 = Y3;\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, EnumsAndAliases) { - std::string Code = "namespace ns {\n" - "enum class X { X1 };\n" - "enum Y { Y1, Y2, Y3 };\n" - "} // namespace ns\n" - "typedef ns::X TX;\n" - "typedef ns::Y TY;\n" - "using UX = ns::X;\n" - "using UY = ns::Y;\n" - "namespace na {\n" - "namespace nb {\n" - "void f() {\n" - " ns::X x1 = ns::X::X1;\n" - " TX tx1 = TX::X1;\n" - " UX ux1 = UX::X1;\n" - " ns::Y y1 = ns::Y::Y1;\n" - " TY ty1 = TY::Y1;\n" - " UY uy1 = UY::Y1;\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace ns {\n" - "enum class X { X1 };\n" - "enum Y { Y1, Y2, Y3 };\n" - "} // namespace ns\n" - "typedef ns::X TX;\n" - "typedef ns::Y TY;\n" - "using UX = ns::X;\n" - "using UY = ns::Y;\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "void f() {\n" - " ns::X x1 = ns::X::X1;\n" - " TX tx1 = TX::X1;\n" - " UX ux1 = UX::X1;\n" - " ns::Y y1 = ns::Y::Y1;\n" - " TY ty1 = TY::Y1;\n" - " UY uy1 = UY::Y1;\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, EnumInClass) { - std::string Code = "namespace na {\n" - "struct X { enum E { E1 }; };\n" - "namespace nb {\n" - "void f() {\n" - " X::E e = X::E1;\n" - " X::E ee = X::E::E1;\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "struct X { enum E { E1 }; };\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "void f() {\n" - " na::X::E e = na::X::E1;\n" - " na::X::E ee = na::X::E::E1;\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, TypeAsTemplateParameter) { - std::string Code = "namespace na {\n" - "struct X {};\n" - "namespace nb {\n" - "template \n" - "void TempTemp(const TT& t) {\n" - " TT tmp;\n" - "}\n" - "template \n" - "void Temp(const T& t) {\n" - " T tmp = t;\n" - " TempTemp(tmp);\n" - " TempTemp(t);\n" - "}\n" - "void f() {\n" - " X x;\n" - " Temp(x);\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "struct X {};\n" - "\n" - "} // namespace na\n" - "namespace x {\n" - "namespace y {\n" - "template \n" - "void TempTemp(const TT& t) {\n" - " TT tmp;\n" - "}\n" - "template \n" - "void Temp(const T& t) {\n" - " T tmp = t;\n" - " TempTemp(tmp);\n" - " TempTemp(t);\n" - "}\n" - "void f() {\n" - " na::X x;\n" - " Temp(x);\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, DefaultMoveConstructors) { - std::string Code = "namespace na {\n" - "class B {\n" - " public:\n" - " B() = default;\n" - " // Allow move only.\n" - " B(B&&) = default;\n" - " B& operator=(B&&) = default;\n" - " B(const B&) = delete;\n" - " B& operator=(const B&) = delete;\n" - " private:\n" - " int ref_;\n" - "};\n" - "} // namespace na\n" - "namespace na {\n" - "namespace nb {\n" - "class A {\n" - "public:\n" - " A() = default;\n" - " A(A&&) = default;\n" - " A& operator=(A&&) = default;\n" - "private:\n" - " B b;\n" - " A(const A&) = delete;\n" - " A& operator=(const A&) = delete;\n" - "};\n" - "void f() { A a; a = A(); A aa = A(); }\n" - "} // namespace nb\n" - "} // namespace na\n"; - std::string Expected = "namespace na {\n" - "class B {\n" - " public:\n" - " B() = default;\n" - " // Allow move only.\n" - " B(B&&) = default;\n" - " B& operator=(B&&) = default;\n" - " B(const B&) = delete;\n" - " B& operator=(const B&) = delete;\n" - " private:\n" - " int ref_;\n" - "};\n" - "} // namespace na\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "class A {\n" - "public:\n" - " A() = default;\n" - " A(A&&) = default;\n" - " A& operator=(A&&) = default;\n" - "private:\n" - " na::B b;\n" - " A(const A&) = delete;\n" - " A& operator=(const A&) = delete;\n" - "};\n" - "void f() { A a; a = A(); A aa = A(); }\n" - "} // namespace y\n" - "} // namespace x\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, InjectedClassNameInFriendDecl) { - OldNamespace = "d"; - NewNamespace = "e"; - std::string Code = "namespace a{\n" - "template \n" - "class Base {\n" - " public:\n" - " void f() {\n" - " T t;\n" - " t.priv();\n" - " }\n" - "};\n" - "} // namespace a\n" - "namespace d {\n" - "class D : public a::Base {\n" - " private:\n" - " friend class Base;\n" - " void priv() {}\n" - " Base b;\n" - "};\n" - "\n" - "void f() {\n" - " D d;\n" - " a:: Base b;\n" - " b.f();\n" - "}\n" - "} // namespace d\n"; - std::string Expected = "namespace a{\n" - "template \n" - "class Base {\n" - " public:\n" - " void f() {\n" - " T t;\n" - " t.priv();\n" - " }\n" - "};\n" - "} // namespace a\n" - "\n" - "namespace e {\n" - "class D : public a::Base {\n" - " private:\n" - " friend class Base;\n" - " void priv() {}\n" - " a::Base b;\n" - "};\n" - "\n" - "void f() {\n" - " D d;\n" - " a::Base b;\n" - " b.f();\n" - "}\n" - "} // namespace e\n"; - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -TEST_F(ChangeNamespaceTest, FullyQualifyConflictNamespace) { - std::string Code = - "namespace x { namespace util { class Some {}; } }\n" - "namespace x { namespace y {namespace base { class Base {}; } } }\n" - "namespace util { class Status {}; }\n" - "namespace base { class Base {}; }\n" - "namespace na {\n" - "namespace nb {\n" - "void f() {\n" - " util::Status s1; x::util::Some s2;\n" - " base::Base b1; x::y::base::Base b2;\n" - "}\n" - "} // namespace nb\n" - "} // namespace na\n"; - - std::string Expected = - "namespace x { namespace util { class Some {}; } }\n" - "namespace x { namespace y {namespace base { class Base {}; } } }\n" - "namespace util { class Status {}; }\n" - "namespace base { class Base {}; }\n" - "\n" - "namespace x {\n" - "namespace y {\n" - "void f() {\n" - " ::util::Status s1; util::Some s2;\n" - " ::base::Base b1; base::Base b2;\n" - "}\n" - "} // namespace y\n" - "} // namespace x\n"; - - EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); -} - -} // anonymous namespace -} // namespace change_namespace -} // namespace clang Index: clang-tools-extra/trunk/unittests/clang-change-namespace/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/unittests/clang-change-namespace/CMakeLists.txt +++ clang-tools-extra/trunk/unittests/clang-change-namespace/CMakeLists.txt @@ -0,0 +1,30 @@ +set(LLVM_LINK_COMPONENTS + support + ) + +get_filename_component(CHANGE_NAMESPACE_SOURCE_DIR + ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-change-namespace REALPATH) +include_directories( + ${CHANGE_NAMESPACE_SOURCE_DIR} + ) + +# We'd like clang/unittests/Tooling/RewriterTestContext.h in the test. +include_directories(${CLANG_SOURCE_DIR}) + +add_extra_unittest(ChangeNamespaceTests + ChangeNamespaceTests.cpp + ) + +target_link_libraries(ChangeNamespaceTests + PRIVATE + clangAST + clangASTMatchers + clangBasic + clangChangeNamespace + clangFormat + clangFrontend + clangRewrite + clangSerialization + clangTooling + clangToolingCore + ) Index: clang-tools-extra/trunk/unittests/clang-change-namespace/ChangeNamespaceTests.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clang-change-namespace/ChangeNamespaceTests.cpp +++ clang-tools-extra/trunk/unittests/clang-change-namespace/ChangeNamespaceTests.cpp @@ -0,0 +1,2281 @@ +//===-- ChangeNamespaceTests.cpp - Change namespace unit tests ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ChangeNamespace.h" +#include "unittests/Tooling/RewriterTestContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/FileSystemOptions.h" +#include "clang/Format/Format.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/PCHContainerOperations.h" +#include "clang/Tooling/Refactoring.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "gtest/gtest.h" +#include +#include +#include + +namespace clang { +namespace change_namespace { +namespace { + +class ChangeNamespaceTest : public ::testing::Test { +public: + std::string runChangeNamespaceOnCode(llvm::StringRef Code) { + clang::RewriterTestContext Context; + clang::FileID ID = Context.createInMemoryFile(FileName, Code); + + std::map FileToReplacements; + change_namespace::ChangeNamespaceTool NamespaceTool( + OldNamespace, NewNamespace, FilePattern, + /*WhiteListedSymbolPatterns*/ {}, &FileToReplacements); + ast_matchers::MatchFinder Finder; + NamespaceTool.registerMatchers(&Finder); + std::unique_ptr Factory = + tooling::newFrontendActionFactory(&Finder); + if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, {"-std=c++11"}, + FileName)) + return ""; + formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite); + return format(Context.getRewrittenText(ID)); + } + + std::string format(llvm::StringRef Code) { + tooling::Replacements Replaces = format::reformat( + format::getLLVMStyle(), Code, {tooling::Range(0, Code.size())}); + auto ChangedCode = tooling::applyAllReplacements(Code, Replaces); + EXPECT_TRUE(static_cast(ChangedCode)); + if (!ChangedCode) { + llvm::errs() << llvm::toString(ChangedCode.takeError()); + return ""; + } + return *ChangedCode; + } + +protected: + std::string FileName = "input.cc"; + std::string OldNamespace = "na::nb"; + std::string NewNamespace = "x::y"; + std::string FilePattern = "input.cc"; +}; + +TEST_F(ChangeNamespaceTest, NoMatchingNamespace) { + std::string Code = "namespace na {\n" + "namespace nx {\n" + "class A {};\n" + "} // namespace nx\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "namespace nx {\n" + "class A {};\n" + "} // namespace nx\n" + "} // namespace na\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, SimpleMoveWithoutTypeRefs) { + std::string Code = "namespace na {\n" + "namespace nb {\n" + "class A {};\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "\n\n" + "namespace x {\n" + "namespace y {\n" + "class A {};\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, NewNsNestedInOldNs) { + NewNamespace = "na::nb::nc"; + std::string Code = "namespace na {\n" + "namespace nb {\n" + "class A {};\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "namespace nb {\n" + "namespace nc {\n" + "class A {};\n" + "} // namespace nc\n" + "} // namespace nb\n" + "} // namespace na\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, NewNsNestedInOldNsWithSurroundingNewLines) { + NewNamespace = "na::nb::nc"; + std::string Code = "namespace na {\n" + "namespace nb {\n" + "\n" + "class A {};\n" + "\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "namespace nb {\n" + "namespace nc {\n" + "\n" + "class A {};\n" + "\n" + "} // namespace nc\n" + "} // namespace nb\n" + "} // namespace na\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, MoveOldNsWithSurroundingNewLines) { + NewNamespace = "nx::ny"; + std::string Code = "namespace na {\n" + "namespace nb {\n" + "\n" + "class A {};\n" + "\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "\n\n" + "namespace nx {\n" + "namespace ny {\n" + "\n" + "class A {};\n" + "\n" + "} // namespace ny\n" + "} // namespace nx\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, NewNsNestedInOldNsWithRefs) { + NewNamespace = "na::nb::nc"; + std::string Code = "namespace na {\n" + "class A {};\n" + "namespace nb {\n" + "class B {};\n" + "class C {};\n" + "void f() { A a; B b; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "class A {};\n" + "namespace nb {\n" + "namespace nc {\n" + "class B {};\n" + "class C {};\n" + "void f() { A a; B b; }\n" + "} // namespace nc\n" + "} // namespace nb\n" + "} // namespace na\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, SimpleMoveIntoAnotherNestedNamespace) { + NewNamespace = "na::nc"; + std::string Code = "namespace na {\n" + "namespace nb {\n" + "class A {};\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "\n" + "namespace nc {\n" + "class A {};\n" + "} // namespace nc\n" + "} // namespace na\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, MoveIntoAnotherNestedNamespaceWithRef) { + NewNamespace = "na::nc"; + std::string Code = "namespace na {\n" + "class A {};\n" + "namespace nb {\n" + "class X { A a; };\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "class A {};\n" + "\n" + "namespace nc {\n" + "class X { A a; };\n" + "} // namespace nc\n" + "} // namespace na\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, MoveIntoExistingNamespaceAndShortenRefs) { + std::string Code = "namespace x {\n" + "namespace z {\n" + "class Z {};\n" + "} // namespace z\n" + "namespace y {\n" + "class T {};\n" + "} // namespace y\n" + "} // namespace x\n" + "namespace na {\n" + "class A{};\n" + "namespace nb {\n" + "class X { A a; x::z::Z zz; x::y::T t; };\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace x {\n" + "namespace z {\n" + "class Z {};\n" + "} // namespace z\n" + "namespace y {\n" + "class T {};\n" + "} // namespace y\n" + "} // namespace x\n" + "namespace na {\n" + "class A {};\n\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "class X { na::A a; z::Z zz; T t; };\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, SimpleMoveNestedNamespace) { + NewNamespace = "na::x::y"; + std::string Code = "namespace na {\n" + "class A {};\n" + "namespace nb {\n" + "class B {};\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "class A {};\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "class B {};\n" + "} // namespace y\n" + "} // namespace x\n" + "} // namespace na\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, SimpleMoveWithTypeRefs) { + std::string Code = "namespace na {\n" + "class C_A {};\n" + "namespace nc {\n" + "class C_C {};" + "} // namespace nc\n" + "namespace nb {\n" + "class C_X {\n" + "public:\n" + " C_A a;\n" + " nc::C_C c;\n" + "};\n" + "class C_Y {\n" + " C_X x;\n" + "};\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "class C_A {};\n" + "namespace nc {\n" + "class C_C {};" + "} // namespace nc\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "class C_X {\n" + "public:\n" + " na::C_A a;\n" + " na::nc::C_C c;\n" + "};\n" + "class C_Y {\n" + " C_X x;\n" + "};\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, TypeLocInTemplateSpecialization) { + std::string Code = "namespace na {\n" + "class A {};\n" + "template \n" + "class B {};\n" + "template \n" + "class Two {};\n" + "namespace nc { class C {}; }\n" + "} // na\n" + "\n" + "namespace na {\n" + "namespace nb {\n" + "void f() {\n" + " B b;\n" + " B b_c;\n" + " Two two;\n" + "}\n" + "} // nb\n" + "} // na\n"; + std::string Expected = "namespace na {\n" + "class A {};\n" + "template \n" + "class B {};\n" + "template \n" + "class Two {};\n" + "namespace nc { class C {}; }\n" + "} // na\n" + "\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() {\n" + " na::B b;\n" + " na::B b_c;\n" + " na::Two two;\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, LeaveForwardDeclarationBehind) { + std::string Code = "namespace na {\n" + "namespace nb {\n" + "class FWD;\n" + "class FWD2;\n" + "class A {\n" + " FWD *fwd;\n" + "};\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "namespace nb {\n" + "class FWD;\n" + "class FWD2;\n" + "} // namespace nb\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "\n" + "class A {\n" + " na::nb::FWD *fwd;\n" + "};\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, InsertForwardDeclsProperly) { + std::string Code = "namespace na {\n" + "namespace nb {\n" + "\n" + "class FWD;\n" + "class FWD2;\n" + "class A {\n" + " FWD *fwd;\n" + "};\n" + "\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "namespace nb {\n" + "class FWD;\n" + "class FWD2;\n" + "} // namespace nb\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "\n" + "class A {\n" + " na::nb::FWD *fwd;\n" + "};\n" + "\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, TemplateClassForwardDeclaration) { + std::string Code = "namespace na {\n" + "namespace nb {\n" + "class FWD;\n" + "template class FWD_TEMP;\n" + "class A {\n" + " FWD *fwd;\n" + "};\n" + "template class TEMP {};\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "namespace nb {\n" + "class FWD;\n" + "template class FWD_TEMP;\n" + "} // namespace nb\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "\n" + "class A {\n" + " na::nb::FWD *fwd;\n" + "};\n" + "template class TEMP {};\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, DontMoveForwardDeclarationInClass) { + std::string Code = "namespace na {\n" + "namespace nb {\n" + "class A {\n" + " class FWD;\n" + " FWD *fwd;\n" + " template class FWD_TEMP;\n" + "};\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "\n\n" + "namespace x {\n" + "namespace y {\n" + "class A {\n" + " class FWD;\n" + " FWD *fwd;\n" + " template class FWD_TEMP;\n" + "};\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, MoveFunctions) { + std::string Code = "namespace na {\n" + "class C_A {};\n" + "namespace nc {\n" + "class C_C {};" + "} // namespace nc\n" + "namespace nb {\n" + "void fwd();\n" + "void f(C_A ca, nc::C_C cc) {\n" + " C_A ca_1 = ca;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace na {\n" + "class C_A {};\n" + "namespace nc {\n" + "class C_C {};" + "} // namespace nc\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "void fwd();\n" + "void f(na::C_A ca, na::nc::C_C cc) {\n" + " na::C_A ca_1 = ca;\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, FixUsingShadowDecl) { + std::string Code = "class GLOB {};\n" + "using BLOG = GLOB;\n" + "namespace na {\n" + "namespace nc {\n" + "class SAME {};\n" + "}\n" + "namespace nd {\n" + "class SAME {};\n" + "}\n" + "namespace nb {\n" + "using nc::SAME;\n" + "using YO = nd::SAME;\n" + "typedef nd::SAME IDENTICAL;\n" + "void f(nd::SAME Same) {}\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "class GLOB {};\n" + "using BLOG = GLOB;\n" + "namespace na {\n" + "namespace nc {\n" + "class SAME {};\n" + "}\n" + "namespace nd {\n" + "class SAME {};\n" + "}\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "using ::na::nc::SAME;\n" + "using YO = na::nd::SAME;\n" + "typedef na::nd::SAME IDENTICAL;\n" + "void f(na::nd::SAME Same) {}\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, DontFixUsingShadowDeclInClasses) { + std::string Code = "namespace na {\n" + "class A {};\n" + "class Base { public: Base() {} void m() {} };\n" + "namespace nb {\n" + "class D : public Base {\n" + "public:\n" + " using AA = A; using B = Base;\n" + " using Base::m; using Base::Base;\n" + "};" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace na {\n" + "class A {};\n" + "class Base { public: Base() {} void m() {} };\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "class D : public na::Base {\n" + "public:\n" + " using AA = na::A; using B = na::Base;\n" + " using Base::m; using Base::Base;\n" + "};" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, TypeInNestedNameSpecifier) { + std::string Code = + "namespace na {\n" + "class C_A {\n" + "public:\n" + " class Nested {\n" + " public:\n" + " static int NestedX;\n" + " static void nestedFunc() {}\n" + " };\n" + "};\n" + "namespace nb {\n" + "class C_X {\n" + " C_A na;\n" + " C_A::Nested nested;\n" + " void f() {\n" + " C_A::Nested::nestedFunc();\n" + " int X = C_A::Nested::NestedX;\n" + " }\n" + "};\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = + "namespace na {\n" + "class C_A {\n" + "public:\n" + " class Nested {\n" + " public:\n" + " static int NestedX;\n" + " static void nestedFunc() {}\n" + " };\n" + "};\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "class C_X {\n" + " na::C_A na;\n" + " na::C_A::Nested nested;\n" + " void f() {\n" + " na::C_A::Nested::nestedFunc();\n" + " int X = na::C_A::Nested::NestedX;\n" + " }\n" + "};\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, FixFunctionNameSpecifiers) { + std::string Code = + "namespace na {\n" + "class A {\n" + "public:\n" + " static void f() {}\n" + " static void g();\n" + "};\n" + "void A::g() {}" + "void a_f() {}\n" + "static void static_f() {}\n" + "namespace nb {\n" + "void f() { a_f(); static_f(); A::f(); }\n" + "void g() { f(); A::g(); }\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = + "namespace na {\n" + "class A {\n" + "public:\n" + " static void f() {}\n" + " static void g();\n" + "};\n" + "void A::g() {}" + "void a_f() {}\n" + "static void static_f() {}\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "void f() { na::a_f(); na::static_f(); na::A::f(); }\n" + "void g() { f(); na::A::g(); }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, FixOverloadedOperatorFunctionNameSpecifiers) { + std::string Code = + "namespace na {\n" + "class A {\n" + "public:\n" + " int x;\n" + " bool operator==(const A &RHS) const { return x == RHS.x; }\n" + "};\n" + "bool operator<(const A &LHS, const A &RHS) { return LHS.x == RHS.x; }\n" + "namespace nb {\n" + "bool f() {\n" + " A x, y;\n" + " auto f = operator<;\n" + " return (x == y) && (x < y) && (operator<(x, y));\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = + "namespace na {\n" + "class A {\n" + "public:\n" + " int x;\n" + " bool operator==(const A &RHS) const { return x == RHS.x; }\n" + "};\n" + "bool operator<(const A &LHS, const A &RHS) { return LHS.x == RHS.x; }\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "bool f() {\n" + " na::A x, y;\n" + " auto f = na::operator<;\n" + // FIXME: function calls to overloaded operators are not fixed now even if + // they are referenced by qualified names. + " return (x == y) && (x < y) && (operator<(x,y));\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, FixNonCallingFunctionReferences) { + std::string Code = "namespace na {\n" + "class A {\n" + "public:\n" + " static void f() {}\n" + "};\n" + "void a_f() {}\n" + "static void s_f() {}\n" + "namespace nb {\n" + "void f() {\n" + " auto *ref1 = A::f;\n" + " auto *ref2 = a_f;\n" + " auto *ref3 = s_f;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = + "namespace na {\n" + "class A {\n" + "public:\n" + " static void f() {}\n" + "};\n" + "void a_f() {}\n" + "static void s_f() {}\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "void f() {\n" + " auto *ref1 = na::A::f;\n" + " auto *ref2 = na::a_f;\n" + " auto *ref3 = na::s_f;\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, MoveAndFixGlobalVariables) { + std::string Code = "namespace na {\n" + "int GlobA;\n" + "static int GlobAStatic = 0;\n" + "namespace nc { int GlobC; }\n" + "namespace nb {\n" + "int GlobB;\n" + "void f() {\n" + " int a = GlobA;\n" + " int b = GlobAStatic;\n" + " int c = nc::GlobC;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace na {\n" + "int GlobA;\n" + "static int GlobAStatic = 0;\n" + "namespace nc { int GlobC; }\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "int GlobB;\n" + "void f() {\n" + " int a = na::GlobA;\n" + " int b = na::GlobAStatic;\n" + " int c = na::nc::GlobC;\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, DoNotFixStaticVariableOfClass) { + std::string Code = "namespace na {\n" + "class A {\n" + "public:\n" + "static int A1;\n" + "static int A2;\n" + "};\n" + "int A::A1 = 0;\n" + "namespace nb {\n" + "void f() {\n" + " int a = A::A1; int b = A::A2;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace na {\n" + "class A {\n" + "public:\n" + "static int A1;\n" + "static int A2;\n" + "};\n" + "int A::A1 = 0;\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "void f() {\n" + " int a = na::A::A1; int b = na::A::A2;\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, NoMisplaceAtEOF) { + std::string Code = "namespace na {\n" + "namespace nb {\n" + "class A;\n" + "class B {};\n" + "}" + "}"; + std::string Expected = "namespace na {\n" + "namespace nb {\n" + "class A;\n" + "}\n" + "}\n" + "namespace x {\n" + "namespace y {\n" + "\n" + "class B {};\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, CommentsBeforeMovedClass) { + std::string Code = "namespace na {\n" + "namespace nb {\n" + "\n\n" + "// Wild comments.\n" + "\n" + "// Comments.\n" + "// More comments.\n" + "class B {\n" + " // Private comments.\n" + " int a;\n" + "};\n" + "}\n" + "}"; + std::string Expected = "\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "\n\n" + "// Wild comments.\n" + "\n" + "// Comments.\n" + "// More comments.\n" + "class B {\n" + " // Private comments.\n" + " int a;\n" + "};\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingShadowDeclInGlobal) { + std::string Code = "namespace glob {\n" + "class Glob {};\n" + "void GFunc() {}\n" + "}\n" + "using glob::Glob;\n" + "using glob::GFunc;\n" + "namespace na {\n" + "namespace nb {\n" + "void f() { Glob g; GFunc(); }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace glob {\n" + "class Glob {};\n" + "void GFunc() {}\n" + "}\n" + "using glob::Glob;\n" + "using glob::GFunc;\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() { Glob g; GFunc(); }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingShadowDeclsInAnonymousNamespaces) { + std::string Code = "namespace util {\n" + "class Util {};\n" + "void func() {}\n" + "}\n" + "namespace na {\n" + "namespace nb {\n" + "namespace {\n" + "using ::util::Util;\n" + "using ::util::func;\n" + "void f() { Util u; func(); }\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace util {\n" + "class Util {};\n" + "void func() {}\n" + "} // namespace util\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "namespace {\n" + "using ::util::Util;\n" + "using ::util::func;\n" + "void f() { Util u; func(); }\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingNamespaceInGlobal) { + std::string Code = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "using namespace glob;\n" + "namespace na {\n" + "namespace nb {\n" + "void f() { Glob g; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "using namespace glob;\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() { Glob g; }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, NamespaceAliasInGlobal) { + std::string Code = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace glob2 { class Glob2 {}; }\n" + "namespace gl = glob;\n" + "namespace gl2 = glob2;\n" + "namespace na {\n" + "namespace nb {\n" + "void f() { gl::Glob g; gl2::Glob2 g2; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = + "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace glob2 { class Glob2 {}; }\n" + "namespace gl = glob;\n" + "namespace gl2 = glob2;\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() { gl::Glob g; gl2::Glob2 g2; }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, NamespaceAliasInNamespace) { + std::string Code = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace na {\n" + "namespace nb {\n" + "namespace gl = glob;\n" + "void f() { gl::Glob g; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "namespace gl = glob;\n" + "void f() { gl::Glob g; }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, NamespaceAliasInAncestorNamespace) { + NewNamespace = "na::nx"; + std::string Code = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace other { namespace gl = glob; }\n" + "namespace na {\n" + "namespace ga = glob;\n" + "namespace nb {\n" + "void f() { ga::Glob g; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace other { namespace gl = glob; }\n" + "namespace na {\n" + "namespace ga = glob;\n" + "\n" + "namespace nx {\n" + "void f() { ga::Glob g; }\n" + "} // namespace nx\n" + "} // namespace na\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, NamespaceAliasInOtherNamespace) { + std::string Code = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace other { namespace gl = glob; }\n" + "namespace na {\n" + "namespace ga = glob;\n" + "namespace nb {\n" + "void f() { glob::Glob g; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace other { namespace gl = glob; }\n" + "namespace na {\n" + "namespace ga = glob;\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "void f() { glob::Glob g; }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingDeclAfterReference) { + std::string Code = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace na {\n" + "namespace nb {\n" + "void f() { glob::Glob g; }\n" + "} // namespace nb\n" + "} // namespace na\n" + "using glob::Glob;\n" + "using namespace glob;\n"; + + std::string Expected = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() { glob::Glob g; }\n" + "} // namespace y\n" + "} // namespace x\n" + "using glob::Glob;\n" + "using namespace glob;\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingNamespaceAfterReference) { + NewNamespace = "na::nc"; + std::string Code = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace na {\n" + "namespace nb {\n" + "void f() { glob::Glob g; }\n" + "} // namespace nb\n" + "using namespace glob;\n" + "} // namespace na\n"; + + std::string Expected = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace na {\n" + "\n" + "namespace nc {\n" + "void f() { glob::Glob g; }\n" + "} // namespace nc\n" + "using namespace glob;\n" + "} // namespace na\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingNamespaceAndUsingShadowInGlobal) { + std::string Code = "namespace glob1 {\n" + "namespace glob2 {\n" + "class Glob {};\n" + "}\n" + "}\n" + "using glob1::glob2::Glob;\n" + "using namespace glob1;\n" + "namespace na {\n" + "namespace nb {\n" + "void f() { Glob g; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace glob1 {\n" + "namespace glob2 {\n" + "class Glob {};\n" + "}\n" + "}\n" + "using glob1::glob2::Glob;\n" + "using namespace glob1;\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() { Glob g; }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingAliasInGlobal) { + std::string Code = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "using GLB = glob::Glob;\n" + "using BLG = glob::Glob;\n" + "namespace na {\n" + "namespace nb {\n" + "void f() { GLB g; BLG blg; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "using GLB = glob::Glob;\n" + "using BLG = glob::Glob;\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() { GLB g; BLG blg; }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingShadowDeclAndMovedNamespace) { + std::string Code = "namespace na { class C_A {};\n }\n" + "using na::C_A;\n" + "namespace na {\n" + "namespace nb {\n" + "class C_X {\n" + "public:\n" + " C_A a;\n" + "};\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na { class C_A {};\n }\n" + "using na::C_A;\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "class C_X {\n" + "public:\n" + " C_A a;\n" + "};\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingNamespaceDeclAndMovedNamespace) { + std::string Code = "namespace na { class C_A {};\n }\n" + "using namespace na;\n" + "namespace na {\n" + "namespace nb {\n" + "class C_X {\n" + "public:\n" + " C_A ca;\n" + "};\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na { class C_A {};\n }\n" + "using namespace na;\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "class C_X {\n" + "public:\n" + " C_A ca;\n" + "};\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingShadowDeclInFunction) { + std::string Code = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace na {\n" + "namespace nb {\n" + "void f() {\n" + " using glob::Glob;\n" + " Glob g;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() {\n" + " using ::glob::Glob;\n" + " Glob g;\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingShadowDeclInClass) { + std::string Code = "namespace na { class C_A {}; }\n" + "namespace na {\n" + "namespace nb {\n" + "void f() {\n" + " using ::na::C_A;\n" + " C_A ca;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na { class C_A {}; }\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() {\n" + " using ::na::C_A;\n" + " C_A ca;\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingDeclInMovedNamespace) { + std::string Code = "namespace nx { void f(); }\n" + "namespace na {\n" + "using nx::f;\n" + "namespace nb {\n" + "void d() { f(); }\n" + "} // nb\n" + "} // na\n"; + + std::string Expected = "namespace nx { void f(); }\n" + "namespace na {\n" + "using nx::f;\n" + "\n" + "} // na\n" + "namespace x {\n" + "namespace y {\n" + "void d() { nx::f(); }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingDeclInMovedNamespaceNotNested) { + OldNamespace = "na"; + std::string Code = "namespace nx { void f(); }\n" + "namespace na {\n" + "using ::nx::f;\n" + "void d() { f(); }\n" + "} // na\n"; + + std::string Expected = "namespace nx { void f(); }\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "using ::nx::f;\n" + "void d() { f(); }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingDeclInMovedNamespaceMultiNested) { + OldNamespace = "a::b::c::d"; + NewNamespace = "a::b::x::y"; + std::string Code = "namespace nx { void f(); void g(); }\n" + "namespace a {\n" + "namespace b {\n" + "using ::nx::f;\n" + "namespace c {\n" + "using ::nx::g;\n" + "namespace d {\n" + "void d() { f(); g(); }\n" + "} // d\n" + "} // c\n" + "} // b\n" + "} // a\n"; + + std::string Expected = "namespace nx { void f(); void g(); }\n" + "namespace a {\n" + "namespace b {\n" + "using ::nx::f;\n" + "namespace c {\n" + "using ::nx::g;\n" + "\n" + "} // c\n" + "namespace x {\n" + "namespace y {\n" + "void d() { f(); nx::g(); }\n" + "} // namespace y\n" + "} // namespace x\n" + "} // b\n" + "} // a\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingShadowDeclInTheParentOfOldNamespace) { + OldNamespace = "nb::nc"; + NewNamespace = "nb::nd"; + std::string Code = "namespace na { class A {}; }\n" + "namespace nb {\n" + "using na::A;\n" + "namespace nc {\n" + "void d() { A a; }\n" + "} // nc\n" + "} // nb\n"; + + std::string Expected = "namespace na { class A {}; }\n" + "namespace nb {\n" + "using na::A;\n" + "\n" + "namespace nd {\n" + "void d() { A a; }\n" + "} // namespace nd\n" + "} // nb\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingShadowDeclInOldNamespace) { + OldNamespace = "nb"; + NewNamespace = "nc"; + std::string Code = "namespace na { class A {}; }\n" + "namespace nb {\n" + "using na::A;\n" + "void d() { A a; }\n" + "struct X { A a; };\n" + "} // nb\n"; + + std::string Expected = "namespace na { class A {}; }\n" + "\n" + "namespace nc {\n" + "using ::na::A;\n" + "void d() { A a; }\n" + "struct X { A a; };\n" + "} // namespace nc\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingShadowDeclOfTemplateClass) { + OldNamespace = "nb"; + NewNamespace = "nc"; + std::string Code = "namespace na {\n" + "template \n" + "class A { T t; };\n" + "} // namespace na\n" + "namespace nb {\n" + "using na::A;\n" + "void d() { A a; }\n" + "} // nb\n"; + + std::string Expected = "namespace na {\n" + "template \n" + "class A { T t; };\n" + "} // namespace na\n" + "\n" + "namespace nc {\n" + "using ::na::A;\n" + "void d() { A a; }\n" + "} // namespace nc\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingShadowDeclOfTemplateFunction) { + OldNamespace = "nb"; + NewNamespace = "nc"; + std::string Code = "namespace na {\n" + "template \n" + "void f() { T t; };\n" + "} // namespace na\n" + "namespace nb {\n" + "using na::f;\n" + "void d() { f(); }\n" + "} // nb\n"; + + std::string Expected = "namespace na {\n" + "template \n" + "void f() { T t; };\n" + "} // namespace na\n" + "\n" + "namespace nc {\n" + "using ::na::f;\n" + "void d() { f(); }\n" + "} // namespace nc\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingAliasDecl) { + std::string Code = + "namespace nx { namespace ny { class X {}; } }\n" + "namespace na {\n" + "namespace nb {\n" + "using Y = nx::ny::X;\n" + "void f() { Y y; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace nx { namespace ny { class X {}; } }\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "using Y = nx::ny::X;\n" + "void f() { Y y; }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingAliasDeclInGlobal) { + std::string Code = + "namespace nx { namespace ny { class X {}; } }\n" + "using Y = nx::ny::X;\n" + "namespace na {\n" + "namespace nb {\n" + "void f() { Y y; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace nx { namespace ny { class X {}; } }\n" + "using Y = nx::ny::X;\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() { Y y; }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + + +TEST_F(ChangeNamespaceTest, TypedefAliasDecl) { + std::string Code = + "namespace nx { namespace ny { class X {}; } }\n" + "namespace na {\n" + "namespace nb {\n" + "typedef nx::ny::X Y;\n" + "void f() { Y y; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace nx { namespace ny { class X {}; } }\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "typedef nx::ny::X Y;\n" + "void f() { Y y; }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, DerivedClassWithConstructors) { + std::string Code = + "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n" + "namespace na {\n" + "namespace nb {\n" + "class A : public nx::ny::X {\n" + "public:\n" + " A() : X(0) {}\n" + " A(int i);\n" + "};\n" + "A::A(int i) : X(i) {}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = + "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n" + "\n\n" + "namespace x {\n" + "namespace y {\n" + "class A : public nx::ny::X {\n" + "public:\n" + " A() : X(0) {}\n" + " A(int i);\n" + "};\n" + "A::A(int i) : X(i) {}\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, DerivedClassWithQualifiedConstructors) { + std::string Code = + "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n" + "namespace na {\n" + "namespace nb {\n" + "class A : public nx::ny::X {\n" + "public:\n" + " A() : X::X(0) {}\n" + " A(int i);\n" + "};\n" + "A::A(int i) : X::X(i) {}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = + "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n" + "\n\n" + "namespace x {\n" + "namespace y {\n" + "class A : public nx::ny::X {\n" + "public:\n" + " A() : X::X(0) {}\n" + " A(int i);\n" + "};\n" + "A::A(int i) : X::X(i) {}\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, DerivedClassWithConstructorsAndTypeRefs) { + std::string Code = + "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n" + "namespace na {\n" + "namespace nb {\n" + "class A : public nx::ny::X {\n" + "public:\n" + " A() : X(0) {}\n" + " A(int i);\n" + "};\n" + "A::A(int i) : X(i) { X x(1);}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = + "namespace nx { namespace ny { class X { public: X(int i) {} }; } }\n" + "\n\n" + "namespace x {\n" + "namespace y {\n" + "class A : public nx::ny::X {\n" + "public:\n" + " A() : X(0) {}\n" + " A(int i);\n" + "};\n" + "A::A(int i) : X(i) { nx::ny::X x(1);}\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, MoveToGlobalNamespace) { + NewNamespace = ""; + std::string Code = "namespace na {\n" + "class C_A {};\n" + "namespace nc {\n" + "class C_C {};" + "} // namespace nc\n" + "namespace nb {\n" + "class C_X {\n" + "public:\n" + " C_A a;\n" + " nc::C_C c;\n" + "};\n" + "class C_Y {\n" + " C_X x;\n" + "};\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "class C_A {};\n" + "namespace nc {\n" + "class C_C {};" + "} // namespace nc\n" + "\n" + "} // namespace na\n" + "class C_X {\n" + "public:\n" + " na::C_A a;\n" + " na::nc::C_C c;\n" + "};\n" + "class C_Y {\n" + " C_X x;\n" + "};\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, KeepGlobalSpecifier) { + std::string Code = "class Glob {};\n" + "namespace na {\n" + "class C_A {};\n" + "namespace nc {\n" + "class C_C {};" + "} // namespace nc\n" + "namespace nb {\n" + "class C_X {\n" + "public:\n" + " ::Glob glob_1;\n" + " Glob glob_2;\n" + " C_A a_1;\n" + " ::na::C_A a_2;\n" + " nc::C_C c;\n" + "};\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "class Glob {};\n" + "namespace na {\n" + "class C_A {};\n" + "namespace nc {\n" + "class C_C {};" + "} // namespace nc\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "class C_X {\n" + "public:\n" + " ::Glob glob_1;\n" + " Glob glob_2;\n" + " na::C_A a_1;\n" + " ::na::C_A a_2;\n" + " na::nc::C_C c;\n" + "};\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, UsingAliasInTemplate) { + NewNamespace = "na::nb::nc"; + std::string Code = "namespace some_ns {\n" + "template \n" + "class G {};\n" + "} // namespace some_ns\n" + "namespace na {\n" + "template\n" + "using GG = some_ns::G;\n" + "} // namespace na\n" + "namespace na {\n" + "namespace nb {\n" + "void f() {\n" + " GG g;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace some_ns {\n" + "template \n" + "class G {};\n" + "} // namespace some_ns\n" + "namespace na {\n" + "template\n" + "using GG = some_ns::G;\n" + "} // namespace na\n" + "namespace na {\n" + "namespace nb {\n" + "namespace nc {\n" + "void f() {\n" + " GG g;\n" + "}\n" + "} // namespace nc\n" + "} // namespace nb\n" + "} // namespace na\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, TemplateUsingAliasInBaseClass) { + NewNamespace = "na::nb::nc"; + std::string Code = "namespace some_ns {\n" + "template \n" + "class G {};\n" + "} // namespace some_ns\n" + "namespace na {\n" + "class Base {\n" + "public:\n" + " template\n" + " using GG = some_ns::G;\n" + "\n" + " struct Nested {};\n" + "};\n" + "class Derived : public Base {};\n" + "} // namespace na\n" + "namespace na {\n" + "namespace nb {\n" + "void f() {\n" + " Derived::GG g;\n" + " const Derived::GG gg;\n" + " const Derived::GG* gg_ptr;\n" + " struct Derived::Nested nested;\n" + " const struct Derived::Nested *nested_ptr;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace some_ns {\n" + "template \n" + "class G {};\n" + "} // namespace some_ns\n" + "namespace na {\n" + "class Base {\n" + "public:\n" + " template\n" + " using GG = some_ns::G;\n" + "\n" + " struct Nested {};\n" + "};\n" + "class Derived : public Base {};\n" + "} // namespace na\n" + "namespace na {\n" + "namespace nb {\n" + "namespace nc {\n" + "void f() {\n" + " Derived::GG g;\n" + " const Derived::GG gg;\n" + " const Derived::GG* gg_ptr;\n" + " struct Derived::Nested nested;\n" + " const struct Derived::Nested *nested_ptr;\n" + "}\n" + "} // namespace nc\n" + "} // namespace nb\n" + "} // namespace na\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, ExistingNamespaceConflictWithNewNamespace) { + OldNamespace = "nx"; + NewNamespace = "ny::na::nc"; + std::string Code = "namespace na {\n" + "class A {};\n" + "} // namespace na\n" + "namespace nb {\n" + "class B {};\n" + "} // namespace nb\n" + "namespace nx {\n" + "class X {\n" + " na::A a; nb::B b;\n" + "};\n" + "} // namespace nx\n"; + std::string Expected = "namespace na {\n" + "class A {};\n" + "} // namespace na\n" + "namespace nb {\n" + "class B {};\n" + "} // namespace nb\n" + "\n" + "namespace ny {\n" + "namespace na {\n" + "namespace nc {\n" + "class X {\n" + " ::na::A a; nb::B b;\n" + "};\n" + "} // namespace nc\n" + "} // namespace na\n" + "} // namespace ny\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, SymbolConflictWithNewNamespace) { + OldNamespace = "nx"; + NewNamespace = "ny::na::nc"; + std::string Code = "namespace na {\n" + "class A {};\n" + "namespace nb {\n" + "class B {};\n" + "} // namespace nb\n" + "} // namespace na\n" + "namespace ny {\n" + "class Y {};\n" + "}\n" + "namespace nx {\n" + "class X {\n" + " na::A a; na::nb::B b;\n" + " ny::Y y;" + "};\n" + "} // namespace nx\n"; + std::string Expected = "namespace na {\n" + "class A {};\n" + "namespace nb {\n" + "class B {};\n" + "} // namespace nb\n" + "} // namespace na\n" + "namespace ny {\n" + "class Y {};\n" + "}\n" + "\n" + "namespace ny {\n" + "namespace na {\n" + "namespace nc {\n" + "class X {\n" + " ::na::A a; ::na::nb::B b;\n" + " Y y;\n" + "};\n" + "} // namespace nc\n" + "} // namespace na\n" + "} // namespace ny\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, ShortenNamespaceSpecifier) { + OldNamespace = "nx"; + NewNamespace = "ny::na"; + std::string Code = "class G {};\n" + "namespace ny {\n" + "class Y {};\n" + "namespace na {\n" + "class A {};\n" + "namespace nc { class C {}; } // namespace nc\n" + "}\n // namespace na\n" + "}\n // namespace ny\n" + "namespace nx {\n" + "class X {\n" + " G g; ny::Y y; ny::na::A a; ny::na::nc::C c;\n" + "};\n" + "} // namespace nx\n"; + std::string Expected = "class G {};\n" + "namespace ny {\n" + "class Y {};\n" + "namespace na {\n" + "class A {};\n" + "namespace nc { class C {}; } // namespace nc\n" + "}\n // namespace na\n" + "}\n // namespace ny\n" + "\n" + "namespace ny {\n" + "namespace na {\n" + "class X {\n" + " G g; Y y; A a; nc::C c;\n" + "};\n" + "} // namespace na\n" + "} // namespace ny\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, ShortenNamespaceSpecifierInAnonymousNamespace) { + OldNamespace = "nx"; + NewNamespace = "ny::na"; + std::string Code = "class G {};\n" + "namespace ny {\n" + "class Y {};\n" + "namespace na {\n" + "class A {};\n" + "namespace nc { class C {}; } // namespace nc\n" + "}\n // namespace na\n" + "}\n // namespace ny\n" + "namespace nx {\n" + "namespace {\n" + "class X {\n" + " G g; ::ny::Y y; ::ny::na::A a; ::ny::na::nc::C c;\n" + "};\n" + "} // namespace\n" + "} // namespace nx\n"; + std::string Expected = "class G {};\n" + "namespace ny {\n" + "class Y {};\n" + "namespace na {\n" + "class A {};\n" + "namespace nc { class C {}; } // namespace nc\n" + "}\n // namespace na\n" + "}\n // namespace ny\n" + "\n" + "namespace ny {\n" + "namespace na {\n" + "namespace {\n" + "class X {\n" + " G g; Y y; A a; nc::C c;\n" + "};\n" + "} // namespace\n" + "} // namespace na\n" + "} // namespace ny\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, SimpleMoveEnum) { + std::string Code = "namespace na {\n" + "namespace nb {\n" + "enum class X { X1, X2 };\n" + "enum Y { Y1, Y2 };\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "\n\nnamespace x {\n" + "namespace y {\n" + "enum class X { X1, X2 };\n" + "enum Y { Y1, Y2 };\n" + "} // namespace y\n" + "} // namespace x\n"; + + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, ReferencesToEnums) { + std::string Code = "enum Glob { G1, G2 };\n" + "namespace na {\n" + "enum class X { X1 };\n" + "enum Y { Y1, Y2 };\n" + "namespace nb {\n" + "void f() {\n" + " Glob g1 = Glob::G1;\n" + " Glob g2 = G2;\n" + " X x1 = X::X1;\n" + " Y y1 = Y::Y1;\n" + " Y y2 = Y2;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "enum Glob { G1, G2 };\n" + "namespace na {\n" + "enum class X { X1 };\n" + "enum Y { Y1, Y2 };\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "void f() {\n" + " Glob g1 = Glob::G1;\n" + " Glob g2 = G2;\n" + " na::X x1 = na::X::X1;\n" + " na::Y y1 = na::Y::Y1;\n" + " na::Y y2 = na::Y2;\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, NoRedundantEnumUpdate) { + std::string Code = "namespace ns {\n" + "enum class X { X1 };\n" + "enum Y { Y1, Y2 };\n" + "} // namespace ns\n" + "namespace na {\n" + "namespace nb {\n" + "void f() {\n" + " ns::X x1 = ns::X::X1;\n" + " ns::Y y1 = ns::Y::Y1;\n" + " ns::Y y2 = ns::Y2;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace ns {\n" + "enum class X { X1 };\n" + "enum Y { Y1, Y2 };\n" + "} // namespace ns\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() {\n" + " ns::X x1 = ns::X::X1;\n" + " ns::Y y1 = ns::Y::Y1;\n" + " ns::Y y2 = ns::Y2;\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + ; + + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, EnumsAndUsingShadows) { + std::string Code = "namespace ns {\n" + "enum class X { X1 };\n" + "enum Y { Y1, Y2, Y3 };\n" + "} // namespace ns\n" + "using ns::X;\n" + "using ns::Y;\n" + "using ns::Y::Y2;\n" + "using ns::Y::Y3;\n" + "namespace na {\n" + "namespace nb {\n" + "void f() {\n" + " X x1 = X::X1;\n" + " Y y1 = Y::Y1;\n" + " Y y2 = Y2;\n" + " Y y3 = Y3;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace ns {\n" + "enum class X { X1 };\n" + "enum Y { Y1, Y2, Y3 };\n" + "} // namespace ns\n" + "using ns::X;\n" + "using ns::Y;\n" + "using ns::Y::Y2;\n" + "using ns::Y::Y3;\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() {\n" + " X x1 = X::X1;\n" + " Y y1 = Y::Y1;\n" + " Y y2 = Y2;\n" + " Y y3 = Y3;\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, EnumsAndAliases) { + std::string Code = "namespace ns {\n" + "enum class X { X1 };\n" + "enum Y { Y1, Y2, Y3 };\n" + "} // namespace ns\n" + "typedef ns::X TX;\n" + "typedef ns::Y TY;\n" + "using UX = ns::X;\n" + "using UY = ns::Y;\n" + "namespace na {\n" + "namespace nb {\n" + "void f() {\n" + " ns::X x1 = ns::X::X1;\n" + " TX tx1 = TX::X1;\n" + " UX ux1 = UX::X1;\n" + " ns::Y y1 = ns::Y::Y1;\n" + " TY ty1 = TY::Y1;\n" + " UY uy1 = UY::Y1;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace ns {\n" + "enum class X { X1 };\n" + "enum Y { Y1, Y2, Y3 };\n" + "} // namespace ns\n" + "typedef ns::X TX;\n" + "typedef ns::Y TY;\n" + "using UX = ns::X;\n" + "using UY = ns::Y;\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() {\n" + " ns::X x1 = ns::X::X1;\n" + " TX tx1 = TX::X1;\n" + " UX ux1 = UX::X1;\n" + " ns::Y y1 = ns::Y::Y1;\n" + " TY ty1 = TY::Y1;\n" + " UY uy1 = UY::Y1;\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, EnumInClass) { + std::string Code = "namespace na {\n" + "struct X { enum E { E1 }; };\n" + "namespace nb {\n" + "void f() {\n" + " X::E e = X::E1;\n" + " X::E ee = X::E::E1;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "struct X { enum E { E1 }; };\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "void f() {\n" + " na::X::E e = na::X::E1;\n" + " na::X::E ee = na::X::E::E1;\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, TypeAsTemplateParameter) { + std::string Code = "namespace na {\n" + "struct X {};\n" + "namespace nb {\n" + "template \n" + "void TempTemp(const TT& t) {\n" + " TT tmp;\n" + "}\n" + "template \n" + "void Temp(const T& t) {\n" + " T tmp = t;\n" + " TempTemp(tmp);\n" + " TempTemp(t);\n" + "}\n" + "void f() {\n" + " X x;\n" + " Temp(x);\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "struct X {};\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "template \n" + "void TempTemp(const TT& t) {\n" + " TT tmp;\n" + "}\n" + "template \n" + "void Temp(const T& t) {\n" + " T tmp = t;\n" + " TempTemp(tmp);\n" + " TempTemp(t);\n" + "}\n" + "void f() {\n" + " na::X x;\n" + " Temp(x);\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, DefaultMoveConstructors) { + std::string Code = "namespace na {\n" + "class B {\n" + " public:\n" + " B() = default;\n" + " // Allow move only.\n" + " B(B&&) = default;\n" + " B& operator=(B&&) = default;\n" + " B(const B&) = delete;\n" + " B& operator=(const B&) = delete;\n" + " private:\n" + " int ref_;\n" + "};\n" + "} // namespace na\n" + "namespace na {\n" + "namespace nb {\n" + "class A {\n" + "public:\n" + " A() = default;\n" + " A(A&&) = default;\n" + " A& operator=(A&&) = default;\n" + "private:\n" + " B b;\n" + " A(const A&) = delete;\n" + " A& operator=(const A&) = delete;\n" + "};\n" + "void f() { A a; a = A(); A aa = A(); }\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace na {\n" + "class B {\n" + " public:\n" + " B() = default;\n" + " // Allow move only.\n" + " B(B&&) = default;\n" + " B& operator=(B&&) = default;\n" + " B(const B&) = delete;\n" + " B& operator=(const B&) = delete;\n" + " private:\n" + " int ref_;\n" + "};\n" + "} // namespace na\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "class A {\n" + "public:\n" + " A() = default;\n" + " A(A&&) = default;\n" + " A& operator=(A&&) = default;\n" + "private:\n" + " na::B b;\n" + " A(const A&) = delete;\n" + " A& operator=(const A&) = delete;\n" + "};\n" + "void f() { A a; a = A(); A aa = A(); }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, InjectedClassNameInFriendDecl) { + OldNamespace = "d"; + NewNamespace = "e"; + std::string Code = "namespace a{\n" + "template \n" + "class Base {\n" + " public:\n" + " void f() {\n" + " T t;\n" + " t.priv();\n" + " }\n" + "};\n" + "} // namespace a\n" + "namespace d {\n" + "class D : public a::Base {\n" + " private:\n" + " friend class Base;\n" + " void priv() {}\n" + " Base b;\n" + "};\n" + "\n" + "void f() {\n" + " D d;\n" + " a:: Base b;\n" + " b.f();\n" + "}\n" + "} // namespace d\n"; + std::string Expected = "namespace a{\n" + "template \n" + "class Base {\n" + " public:\n" + " void f() {\n" + " T t;\n" + " t.priv();\n" + " }\n" + "};\n" + "} // namespace a\n" + "\n" + "namespace e {\n" + "class D : public a::Base {\n" + " private:\n" + " friend class Base;\n" + " void priv() {}\n" + " a::Base b;\n" + "};\n" + "\n" + "void f() {\n" + " D d;\n" + " a::Base b;\n" + " b.f();\n" + "}\n" + "} // namespace e\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, FullyQualifyConflictNamespace) { + std::string Code = + "namespace x { namespace util { class Some {}; } }\n" + "namespace x { namespace y {namespace base { class Base {}; } } }\n" + "namespace util { class Status {}; }\n" + "namespace base { class Base {}; }\n" + "namespace na {\n" + "namespace nb {\n" + "void f() {\n" + " util::Status s1; x::util::Some s2;\n" + " base::Base b1; x::y::base::Base b2;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = + "namespace x { namespace util { class Some {}; } }\n" + "namespace x { namespace y {namespace base { class Base {}; } } }\n" + "namespace util { class Status {}; }\n" + "namespace base { class Base {}; }\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() {\n" + " ::util::Status s1; util::Some s2;\n" + " ::base::Base b1; base::Base b2;\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +} // anonymous namespace +} // namespace change_namespace +} // namespace clang