Page MenuHomePhabricator

trunk
No OneTemporary

File Metadata

Created
Fri, Jan 24, 4:15 PM
Index: change-namespace/ChangeNamespace.cpp
===================================================================
--- change-namespace/ChangeNamespace.cpp (revision 327628)
+++ change-namespace/ChangeNamespace.cpp (revision 327629)
@@ -1,1015 +1,1015 @@
//===-- ChangeNamespace.cpp - Change namespace implementation -------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ChangeNamespace.h"
#include "clang/Format/Format.h"
#include "clang/Lex/Lexer.h"
#include "llvm/Support/ErrorHandling.h"
using namespace clang::ast_matchers;
namespace clang {
namespace change_namespace {
namespace {
inline std::string
joinNamespaces(const llvm::SmallVectorImpl<StringRef> &Namespaces) {
if (Namespaces.empty())
return "";
std::string Result = Namespaces.front();
for (auto I = Namespaces.begin() + 1, E = Namespaces.end(); I != E; ++I)
Result += ("::" + *I).str();
return Result;
}
// Given "a::b::c", returns {"a", "b", "c"}.
llvm::SmallVector<llvm::StringRef, 4> splitSymbolName(llvm::StringRef Name) {
llvm::SmallVector<llvm::StringRef, 4> Splitted;
Name.split(Splitted, "::", /*MaxSplit=*/-1,
/*KeepEmpty=*/false);
return Splitted;
}
SourceLocation startLocationForType(TypeLoc TLoc) {
// For elaborated types (e.g. `struct a::A`) we want the portion after the
// `struct` but including the namespace qualifier, `a::`.
if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) {
NestedNameSpecifierLoc NestedNameSpecifier =
TLoc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
if (NestedNameSpecifier.getNestedNameSpecifier())
return NestedNameSpecifier.getBeginLoc();
TLoc = TLoc.getNextTypeLoc();
}
return TLoc.getLocStart();
}
SourceLocation endLocationForType(TypeLoc TLoc) {
// Dig past any namespace or keyword qualifications.
while (TLoc.getTypeLocClass() == TypeLoc::Elaborated ||
TLoc.getTypeLocClass() == TypeLoc::Qualified)
TLoc = TLoc.getNextTypeLoc();
// The location for template specializations (e.g. Foo<int>) includes the
// templated types in its location range. We want to restrict this to just
// before the `<` character.
if (TLoc.getTypeLocClass() == TypeLoc::TemplateSpecialization)
return TLoc.castAs<TemplateSpecializationTypeLoc>()
.getLAngleLoc()
.getLocWithOffset(-1);
return TLoc.getEndLoc();
}
// Returns the containing namespace of `InnerNs` by skipping `PartialNsName`.
// If the `InnerNs` does not have `PartialNsName` as suffix, or `PartialNsName`
// is empty, nullptr is returned.
// For example, if `InnerNs` is "a::b::c" and `PartialNsName` is "b::c", then
// the NamespaceDecl of namespace "a" will be returned.
const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs,
llvm::StringRef PartialNsName) {
if (!InnerNs || PartialNsName.empty())
return nullptr;
const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs);
const auto *CurrentNs = InnerNs;
auto PartialNsNameSplitted = splitSymbolName(PartialNsName);
while (!PartialNsNameSplitted.empty()) {
// Get the inner-most namespace in CurrentContext.
while (CurrentContext && !llvm::isa<NamespaceDecl>(CurrentContext))
CurrentContext = CurrentContext->getParent();
if (!CurrentContext)
return nullptr;
CurrentNs = llvm::cast<NamespaceDecl>(CurrentContext);
if (PartialNsNameSplitted.back() != CurrentNs->getNameAsString())
return nullptr;
PartialNsNameSplitted.pop_back();
CurrentContext = CurrentContext->getParent();
}
return CurrentNs;
}
static std::unique_ptr<Lexer>
getLexerStartingFromLoc(SourceLocation Loc, const SourceManager &SM,
const LangOptions &LangOpts) {
if (Loc.isMacroID() &&
!Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc))
return nullptr;
// Break down the source location.
std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
// Try to load the file buffer.
bool InvalidTemp = false;
llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
if (InvalidTemp)
return nullptr;
const char *TokBegin = File.data() + LocInfo.second;
// Lex from the start of the given location.
return llvm::make_unique<Lexer>(SM.getLocForStartOfFile(LocInfo.first),
LangOpts, File.begin(), TokBegin, File.end());
}
// FIXME: get rid of this helper function if this is supported in clang-refactor
// library.
static SourceLocation getStartOfNextLine(SourceLocation Loc,
const SourceManager &SM,
const LangOptions &LangOpts) {
std::unique_ptr<Lexer> Lex = getLexerStartingFromLoc(Loc, SM, LangOpts);
if (!Lex.get())
return SourceLocation();
llvm::SmallVector<char, 16> Line;
// FIXME: this is a bit hacky to get ReadToEndOfLine work.
Lex->setParsingPreprocessorDirective(true);
Lex->ReadToEndOfLine(&Line);
auto End = Loc.getLocWithOffset(Line.size());
return SM.getLocForEndOfFile(SM.getDecomposedLoc(Loc).first) == End
? End
: End.getLocWithOffset(1);
}
// Returns `R` with new range that refers to code after `Replaces` being
// applied.
tooling::Replacement
getReplacementInChangedCode(const tooling::Replacements &Replaces,
const tooling::Replacement &R) {
unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset());
unsigned NewEnd =
Replaces.getShiftedCodePosition(R.getOffset() + R.getLength());
return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,
R.getReplacementText());
}
// Adds a replacement `R` into `Replaces` or merges it into `Replaces` by
// applying all existing Replaces first if there is conflict.
void addOrMergeReplacement(const tooling::Replacement &R,
tooling::Replacements *Replaces) {
auto Err = Replaces->add(R);
if (Err) {
llvm::consumeError(std::move(Err));
auto Replace = getReplacementInChangedCode(*Replaces, R);
*Replaces = Replaces->merge(tooling::Replacements(Replace));
}
}
tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End,
llvm::StringRef ReplacementText,
const SourceManager &SM) {
if (!Start.isValid() || !End.isValid()) {
llvm::errs() << "start or end location were invalid\n";
return tooling::Replacement();
}
if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) {
llvm::errs()
<< "start or end location were in different macro expansions\n";
return tooling::Replacement();
}
Start = SM.getSpellingLoc(Start);
End = SM.getSpellingLoc(End);
if (SM.getFileID(Start) != SM.getFileID(End)) {
llvm::errs() << "start or end location were in different files\n";
return tooling::Replacement();
}
return tooling::Replacement(
SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
SM.getSpellingLoc(End)),
ReplacementText);
}
void addReplacementOrDie(
SourceLocation Start, SourceLocation End, llvm::StringRef ReplacementText,
const SourceManager &SM,
std::map<std::string, tooling::Replacements> *FileToReplacements) {
const auto R = createReplacement(Start, End, ReplacementText, SM);
auto Err = (*FileToReplacements)[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->getLocStart());
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 "::".
bool conflictInNamespace(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 NsSplitted = splitSymbolName(Namespace.trim(":"));
assert(!NsSplitted.empty());
// 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.
for (auto I = NsSplitted.begin() + 1, E = NsSplitted.end(); I != E; ++I) {
if (*I == SymbolSplitted.front())
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<std::string> WhiteListedSymbolPatterns,
std::map<std::string, tooling::Replacements> *FileToReplacements,
llvm::StringRef FallbackStyle)
: FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements),
OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')),
FilePattern(FilePattern), FilePatternRE(FilePattern) {
FileToReplacements->clear();
auto OldNsSplitted = splitSymbolName(OldNamespace);
auto NewNsSplitted = splitSymbolName(NewNamespace);
// Calculates `DiffOldNamespace` and `DiffNewNamespace`.
while (!OldNsSplitted.empty() && !NewNsSplitted.empty() &&
OldNsSplitted.front() == NewNsSplitted.front()) {
OldNsSplitted.erase(OldNsSplitted.begin());
NewNsSplitted.erase(NewNsSplitted.begin());
}
DiffOldNamespace = joinNamespaces(OldNsSplitted);
DiffNewNamespace = joinNamespaces(NewNsSplitted);
for (const auto &Pattern : 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<llvm::StringRef, 4> DiffOldNsSplitted;
llvm::StringRef(DiffOldNamespace)
.split(DiffOldNsSplitted, "::", /*MaxSplit=*/-1,
/*KeepEmpty=*/false);
std::string Prefix = "-";
if (!DiffOldNsSplitted.empty())
Prefix = (StringRef(FullOldNs).drop_back(DiffOldNamespace.size()) +
DiffOldNsSplitted.front())
.str();
auto IsInMovedNs =
allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")),
isExpansionInFileMatching(FilePattern));
auto IsVisibleInNewNs = anyOf(
IsInMovedNs, unless(hasAncestor(namespaceDecl(hasName(Prefix)))));
// Match using declarations.
Finder->addMatcher(
usingDecl(isExpansionInFileMatching(FilePattern), IsVisibleInNewNs)
.bind("using"),
this);
// Match using namespace declarations.
Finder->addMatcher(usingDirectiveDecl(isExpansionInFileMatching(FilePattern),
IsVisibleInNewNs)
.bind("using_namespace"),
this);
// Match namespace alias declarations.
Finder->addMatcher(namespaceAliasDecl(isExpansionInFileMatching(FilePattern),
IsVisibleInNewNs)
.bind("namespace_alias"),
this);
// Match old namespace blocks.
Finder->addMatcher(
namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern))
.bind("old_ns"),
this);
// Match class forward-declarations in the old namespace.
// Note that forward-declarations in classes are not matched.
Finder->addMatcher(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())),
IsInMovedNs, hasParent(namespaceDecl()))
.bind("class_fwd_decl"),
this);
// Match template class forward-declarations in the old namespace.
Finder->addMatcher(
classTemplateDecl(unless(hasDescendant(cxxRecordDecl(isDefinition()))),
IsInMovedNs, hasParent(namespaceDecl()))
.bind("template_class_fwd_decl"),
this);
// Match references to types that are not defined in the old namespace.
// Forward-declarations in the old namespace are also matched since they will
// be moved back to the old namespace.
auto DeclMatcher = namedDecl(
hasAncestor(namespaceDecl()),
unless(anyOf(
isImplicit(), hasAncestor(namespaceDecl(isAnonymous())),
hasAncestor(cxxRecordDecl()),
allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition())))))));
// Using shadow declarations in classes always refers to base class, which
// does not need to be qualified since it can be inferred from inheritance.
// Note that this does not match using alias declarations.
auto UsingShadowDeclInClass =
usingDecl(hasAnyUsingShadowDecl(decl()), hasParent(cxxRecordDecl()));
// Match TypeLocs on the declaration. Carefully match only the outermost
// TypeLoc and template specialization arguments (which are not outermost)
// that are directly linked to types matching `DeclMatcher`. Nested name
// specifier locs are handled separately below.
Finder->addMatcher(
typeLoc(IsInMovedNs,
loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))),
unless(anyOf(hasParent(typeLoc(loc(qualType(
allOf(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(decl(forEachDescendant(expr(anyOf(
- callExpr(callee(FuncMatcher)).bind("call"),
- declRefExpr(to(FuncMatcher.bind("func_decl")))
- .bind("func_ref")))),
- IsInMovedNs, unless(isImplicit()))
- .bind("dc"),
- this);
+ Finder->addMatcher(
+ expr(allOf(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<UsingDecl>("using")) {
UsingDecls.insert(Using);
} else if (const auto *UsingNamespace =
Result.Nodes.getNodeAs<UsingDirectiveDecl>(
"using_namespace")) {
UsingNamespaceDecls.insert(UsingNamespace);
} else if (const auto *NamespaceAlias =
Result.Nodes.getNodeAs<NamespaceAliasDecl>(
"namespace_alias")) {
NamespaceAliasDecls.insert(NamespaceAlias);
} else if (const auto *NsDecl =
Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) {
moveOldNamespace(Result, NsDecl);
} else if (const auto *FwdDecl =
Result.Nodes.getNodeAs<CXXRecordDecl>("class_fwd_decl")) {
moveClassForwardDeclaration(Result, cast<NamedDecl>(FwdDecl));
} else if (const auto *TemplateFwdDecl =
Result.Nodes.getNodeAs<ClassTemplateDecl>(
"template_class_fwd_decl")) {
moveClassForwardDeclaration(Result, cast<NamedDecl>(TemplateFwdDecl));
} else if (const auto *UsingWithShadow =
Result.Nodes.getNodeAs<UsingDecl>("using_with_shadow")) {
fixUsingShadowDecl(Result, UsingWithShadow);
} else if (const auto *Specifier =
Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
"nested_specifier_loc")) {
SourceLocation Start = Specifier->getBeginLoc();
SourceLocation End = endLocationForType(Specifier->getTypeLoc());
fixTypeLoc(Result, Start, End, Specifier->getTypeLoc());
} else if (const auto *BaseInitializer =
Result.Nodes.getNodeAs<CXXCtorInitializer>(
"base_initializer")) {
BaseCtorInitializerTypeLocs.push_back(
BaseInitializer->getTypeSourceInfo()->getTypeLoc());
} else if (const auto *TLoc = Result.Nodes.getNodeAs<TypeLoc>("type")) {
// This avoids fixing types with record types as qualifier, which is not
// filtered by matchers in some cases, e.g. the type is templated. We should
// handle the record type qualifier instead.
TypeLoc Loc = *TLoc;
while (Loc.getTypeLocClass() == TypeLoc::Qualified)
Loc = Loc.getNextTypeLoc();
if (Loc.getTypeLocClass() == TypeLoc::Elaborated) {
NestedNameSpecifierLoc NestedNameSpecifier =
Loc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
// 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<DeclRefExpr>("var_ref")) {
const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl");
assert(Var);
if (Var->getCanonicalDecl()->isStaticDataMember())
return;
const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
assert(Context && "Empty decl context.");
fixDeclRefExpr(Result, Context->getDeclContext(),
llvm::cast<NamedDecl>(Var), VarRef);
} else if (const auto *EnumConstRef =
Result.Nodes.getNodeAs<DeclRefExpr>("enum_const_ref")) {
// Do not rename the reference if it is already scoped by the EnumDecl name.
if (EnumConstRef->hasQualifier() &&
EnumConstRef->getQualifier()->getKind() ==
NestedNameSpecifier::SpecifierKind::TypeSpec &&
EnumConstRef->getQualifier()->getAsType()->isEnumeralType())
return;
const auto *EnumConstDecl =
Result.Nodes.getNodeAs<EnumConstantDecl>("enum_const_decl");
assert(EnumConstDecl);
const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
assert(Context && "Empty decl context.");
// FIXME: this would qualify "ns::VALUE" as "ns::EnumValue::VALUE". Fix it
// if it turns out to be an issue.
fixDeclRefExpr(Result, Context->getDeclContext(),
llvm::cast<NamedDecl>(EnumConstDecl), EnumConstRef);
} else if (const auto *FuncRef =
Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
// If this reference has been processed as a function call, we do not
// process it again.
if (ProcessedFuncRefs.count(FuncRef))
return;
ProcessedFuncRefs.insert(FuncRef);
const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
assert(Func);
const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
assert(Context && "Empty decl context.");
fixDeclRefExpr(Result, Context->getDeclContext(),
llvm::cast<NamedDecl>(Func), FuncRef);
} else {
const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
assert(Call != nullptr && "Expecting callback for CallExpr.");
const auto *CalleeFuncRef =
llvm::cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit());
ProcessedFuncRefs.insert(CalleeFuncRef);
const FunctionDecl *Func = Call->getDirectCallee();
assert(Func != nullptr);
// FIXME: ignore overloaded operators. This would miss cases where operators
// are called by qualified names (i.e. "ns::operator <"). Ignore such
// cases for now.
if (Func->isOverloadedOperator())
return;
// Ignore out-of-line static methods since they will be handled by nested
// name specifiers.
if (Func->getCanonicalDecl()->getStorageClass() ==
StorageClass::SC_Static &&
Func->isOutOfLine())
return;
const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
assert(Context && "Empty decl context.");
SourceRange CalleeRange = Call->getCallee()->getSourceRange();
replaceQualifiedSymbolInDeclContext(
Result, Context->getDeclContext(), CalleeRange.getBegin(),
CalleeRange.getEnd(), llvm::cast<NamedDecl>(Func));
}
}
static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl,
const SourceManager &SM,
const LangOptions &LangOpts) {
std::unique_ptr<Lexer> Lex =
getLexerStartingFromLoc(NsDecl->getLocStart(), 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->getLocStart();
SourceLocation End = FwdDecl->getLocEnd();
const SourceManager &SM = *Result.SourceManager;
SourceLocation AfterSemi = Lexer::findLocationAfterToken(
End, tok::semi, SM, Result.Context->getLangOpts(),
/*SkipTrailingWhitespaceAndNewLine=*/true);
if (AfterSemi.isValid())
End = AfterSemi.getLocWithOffset(-1);
// Delete the forward declaration from the code to be moved.
addReplacementOrDie(Start, End, "", SM, &FileToReplacements);
llvm::StringRef Code = Lexer::getSourceText(
CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
SM.getSpellingLoc(End)),
SM, Result.Context->getLangOpts());
// Insert the forward declaration back into the old namespace after moving the
// code from old namespace to new namespace.
// Insertion information is stored in `InsertFwdDecls` and actual
// insertion will be performed in `onEndOfTranslationUnit`.
// Get the (old) namespace that contains the forward declaration.
const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl");
// The namespace contains the forward declaration, so it must not be empty.
assert(!NsDecl->decls_empty());
const auto Insertion = createInsertion(
getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()),
Code, SM);
InsertForwardDeclaration InsertFwd;
InsertFwd.InsertionOffset = Insertion.getOffset();
InsertFwd.ForwardDeclText = Insertion.getReplacementText().str();
InsertFwdDecls[Insertion.getFilePath()].push_back(InsertFwd);
}
// Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p
// FromDecl with the shortest qualified name possible when the reference is in
// `NewNamespace`.
void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
const ast_matchers::MatchFinder::MatchResult &Result,
const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End,
const NamedDecl *FromDecl) {
const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext();
if (llvm::isa<TranslationUnitDecl>(NsDeclContext)) {
// This should not happen in usual unless the TypeLoc is in function type
// parameters, e.g `std::function<void(T)>`. In this case, DeclContext of
// `T` will be the translation unit. We simply use fully-qualified name
// here.
// Note that `FromDecl` must not be defined in the old namespace (according
// to `DeclMatcher`), so its fully-qualified name will not change after
// changing the namespace.
addReplacementOrDie(Start, End, FromDecl->getQualifiedNameAsString(),
*Result.SourceManager, &FileToReplacements);
return;
}
const auto *NsDecl = llvm::cast<NamespaceDecl>(NsDeclContext);
// Calculate the name of the `NsDecl` after it is moved to new namespace.
std::string OldNs = NsDecl->getQualifiedNameAsString();
llvm::StringRef Postfix = OldNs;
bool Consumed = Postfix.consume_front(OldNamespace);
assert(Consumed && "Expect OldNS to start with OldNamespace.");
(void)Consumed;
const std::string NewNs = (NewNamespace + Postfix).str();
llvm::StringRef NestedName = Lexer::getSourceText(
CharSourceRange::getTokenRange(
Result.SourceManager->getSpellingLoc(Start),
Result.SourceManager->getSpellingLoc(End)),
*Result.SourceManager, Result.Context->getLangOpts());
std::string FromDeclName = FromDecl->getQualifiedNameAsString();
for (llvm::Regex &RE : 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;
}
}
}
}
// If the new nested name in the new namespace is the same as it was in the
// old namespace, we don't create replacement.
if (NestedName == ReplaceName ||
(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() &&
conflictInNamespace(ReplaceName, NewNamespace))
ReplaceName = "::" + ReplaceName;
addReplacementOrDie(Start, End, ReplaceName, *Result.SourceManager,
&FileToReplacements);
}
// Replace the [Start, End] of `Type` with the shortest qualified name when the
// `Type` is in `NewNamespace`.
void ChangeNamespaceTool::fixTypeLoc(
const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start,
SourceLocation End, TypeLoc Type) {
// FIXME: do not rename template parameter.
if (Start.isInvalid() || End.isInvalid())
return;
// Types of CXXCtorInitializers do not need to be fixed.
if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type))
return;
if (isTemplateParameter(Type))
return;
// The declaration which this TypeLoc refers to.
const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl");
// `hasDeclaration` gives underlying declaration, but if the type is
// a typedef type, we need to use the typedef type instead.
auto IsInMovedNs = [&](const NamedDecl *D) {
if (!llvm::StringRef(D->getQualifiedNameAsString())
.startswith(OldNamespace + "::"))
return false;
auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getLocStart());
if (ExpansionLoc.isInvalid())
return false;
llvm::StringRef Filename = Result.SourceManager->getFilename(ExpansionLoc);
return FilePatternRE.match(Filename);
};
// Make `FromDecl` the immediate declaration that `Type` refers to, i.e. if
// `Type` is an alias type, we make `FromDecl` the type alias declaration.
// Also, don't fix the \p Type if it refers to a type alias decl in the moved
// namespace since the alias decl will be moved along with the type reference.
if (auto *Typedef = Type.getType()->getAs<TypedefType>()) {
FromDecl = Typedef->getDecl();
if (IsInMovedNs(FromDecl))
return;
} else if (auto *TemplateType =
Type.getType()->getAs<TemplateSpecializationType>()) {
if (TemplateType->isTypeAlias()) {
FromDecl = TemplateType->getTemplateName().getAsTemplateDecl();
if (IsInMovedNs(FromDecl))
return;
}
}
const auto *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc");
assert(DeclCtx && "Empty decl context.");
replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start,
End, FromDecl);
}
void ChangeNamespaceTool::fixUsingShadowDecl(
const ast_matchers::MatchFinder::MatchResult &Result,
const UsingDecl *UsingDeclaration) {
SourceLocation Start = UsingDeclaration->getLocStart();
SourceLocation End = UsingDeclaration->getLocEnd();
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("file", 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: unittests/change-namespace/ChangeNamespaceTests.cpp
===================================================================
--- unittests/change-namespace/ChangeNamespaceTests.cpp (revision 327628)
+++ unittests/change-namespace/ChangeNamespaceTests.cpp (revision 327629)
@@ -1,2213 +1,2249 @@
//===-- ChangeNamespaceTests.cpp - Change namespace unit tests ---*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ChangeNamespace.h"
#include "unittests/Tooling/RewriterTestContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/FileSystemOptions.h"
#include "clang/Basic/VirtualFileSystem.h"
#include "clang/Format/Format.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/PCHContainerOperations.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MemoryBuffer.h"
#include "gtest/gtest.h"
#include <memory>
#include <string>
#include <vector>
namespace clang {
namespace change_namespace {
namespace {
class ChangeNamespaceTest : public ::testing::Test {
public:
std::string runChangeNamespaceOnCode(llvm::StringRef Code) {
clang::RewriterTestContext Context;
clang::FileID ID = Context.createInMemoryFile(FileName, Code);
std::map<std::string, tooling::Replacements> FileToReplacements;
change_namespace::ChangeNamespaceTool NamespaceTool(
OldNamespace, NewNamespace, FilePattern,
/*WhiteListedSymbolPatterns*/ {}, &FileToReplacements);
ast_matchers::MatchFinder Finder;
NamespaceTool.registerMatchers(&Finder);
std::unique_ptr<tooling::FrontendActionFactory> 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<bool>(ChangedCode));
if (!ChangedCode) {
llvm::errs() << llvm::toString(ChangedCode.takeError());
return "";
}
return *ChangedCode;
}
protected:
std::string FileName = "input.cc";
std::string OldNamespace = "na::nb";
std::string NewNamespace = "x::y";
std::string FilePattern = "input.cc";
};
TEST_F(ChangeNamespaceTest, NoMatchingNamespace) {
std::string Code = "namespace na {\n"
"namespace nx {\n"
"class A {};\n"
"} // namespace nx\n"
"} // namespace na\n";
std::string Expected = "namespace na {\n"
"namespace nx {\n"
"class A {};\n"
"} // namespace nx\n"
"} // namespace na\n";
EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
}
TEST_F(ChangeNamespaceTest, SimpleMoveWithoutTypeRefs) {
std::string Code = "namespace na {\n"
"namespace nb {\n"
"class A {};\n"
"} // namespace nb\n"
"} // namespace na\n";
std::string Expected = "\n\n"
"namespace x {\n"
"namespace y {\n"
"class A {};\n"
"} // namespace y\n"
"} // namespace x\n";
EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
}
TEST_F(ChangeNamespaceTest, 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 <typename T>\n"
"class B {};\n"
"template <typename T1, typename T2>\n"
"class Two {};\n"
"namespace nc { class C {}; }\n"
"} // na\n"
"\n"
"namespace na {\n"
"namespace nb {\n"
"void f() {\n"
" B<A> b;\n"
" B<nc::C> b_c;\n"
" Two<A, nc::C> two;\n"
"}\n"
"} // nb\n"
"} // na\n";
std::string Expected = "namespace na {\n"
"class A {};\n"
"template <typename T>\n"
"class B {};\n"
"template <typename T1, typename T2>\n"
"class Two {};\n"
"namespace nc { class C {}; }\n"
"} // na\n"
"\n"
"\n"
"namespace x {\n"
"namespace y {\n"
"void f() {\n"
" na::B<na::A> b;\n"
" na::B<na::nc::C> b_c;\n"
" na::Two<na::A, na::nc::C> 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<typename T> class FWD_TEMP;\n"
"class A {\n"
" FWD *fwd;\n"
"};\n"
"template<typename T> class TEMP {};\n"
"} // namespace nb\n"
"} // namespace na\n";
std::string Expected = "namespace na {\n"
"namespace nb {\n"
"class FWD;\n"
"template<typename T> 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<typename T> 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<typename T> 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<typename T> 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; }\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; }\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 <typename T>\n"
"class A { T t; };\n"
"} // namespace na\n"
"namespace nb {\n"
"using na::A;\n"
"void d() { A<int> a; }\n"
"} // nb\n";
std::string Expected = "namespace na {\n"
"template <typename T>\n"
"class A { T t; };\n"
"} // namespace na\n"
"\n"
"namespace nc {\n"
"using ::na::A;\n"
"void d() { A<int> 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 <typename T>\n"
"void f() { T t; };\n"
"} // namespace na\n"
"namespace nb {\n"
"using na::f;\n"
"void d() { f<int>(); }\n"
"} // nb\n";
std::string Expected = "namespace na {\n"
"template <typename T>\n"
"void f() { T t; };\n"
"} // namespace na\n"
"\n"
"namespace nc {\n"
"using ::na::f;\n"
"void d() { f<int>(); }\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 <typename T, typename S>\n"
"class G {};\n"
"} // namespace some_ns\n"
"namespace na {\n"
"template<typename P>\n"
"using GG = some_ns::G<int, P>;\n"
"} // namespace na\n"
"namespace na {\n"
"namespace nb {\n"
"void f() {\n"
" GG<float> g;\n"
"}\n"
"} // namespace nb\n"
"} // namespace na\n";
std::string Expected = "namespace some_ns {\n"
"template <typename T, typename S>\n"
"class G {};\n"
"} // namespace some_ns\n"
"namespace na {\n"
"template<typename P>\n"
"using GG = some_ns::G<int, P>;\n"
"} // namespace na\n"
"namespace na {\n"
"namespace nb {\n"
"namespace nc {\n"
"void f() {\n"
" GG<float> 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 <typename T, typename S>\n"
"class G {};\n"
"} // namespace some_ns\n"
"namespace na {\n"
"class Base {\n"
"public:\n"
" template<typename P>\n"
" using GG = some_ns::G<int, P>;\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<float> g;\n"
" const Derived::GG<int> gg;\n"
" const Derived::GG<int>* 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 <typename T, typename S>\n"
"class G {};\n"
"} // namespace some_ns\n"
"namespace na {\n"
"class Base {\n"
"public:\n"
" template<typename P>\n"
" using GG = some_ns::G<int, P>;\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<float> g;\n"
" const Derived::GG<int> gg;\n"
" const Derived::GG<int>* 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 <typename TT>\n"
"void TempTemp(const TT& t) {\n"
" TT tmp;\n"
"}\n"
"template <typename T>\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 <typename TT>\n"
"void TempTemp(const TT& t) {\n"
" TT tmp;\n"
"}\n"
"template <typename T>\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 <typename T>\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<D> {\n"
" private:\n"
" friend class Base<D>;\n"
" void priv() {}\n"
" Base b;\n"
"};\n"
"\n"
"void f() {\n"
" D d;\n"
" a:: Base<D> b;\n"
" b.f();\n"
"}\n"
"} // namespace d\n";
std::string Expected = "namespace a{\n"
"template <typename T>\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<D> {\n"
" private:\n"
" friend class Base<D>;\n"
" void priv() {}\n"
" a::Base b;\n"
"};\n"
"\n"
"void f() {\n"
" D d;\n"
" a::Base<D> b;\n"
" b.f();\n"
"}\n"
"} // namespace e\n";
EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
}
} // anonymous namespace
} // namespace change_namespace
} // namespace clang

Event Timeline