summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang-tools-extra/clang-rename/CMakeLists.txt1
-rw-r--r--clang-tools-extra/clang-rename/RenamingAction.cpp42
-rw-r--r--clang-tools-extra/clang-rename/RenamingAction.h22
-rw-r--r--clang-tools-extra/clang-rename/USRFinder.cpp3
-rw-r--r--clang-tools-extra/clang-rename/USRFindingAction.cpp4
-rw-r--r--clang-tools-extra/clang-rename/USRLocFinder.cpp343
-rw-r--r--clang-tools-extra/clang-rename/USRLocFinder.h14
-rw-r--r--clang-tools-extra/unittests/clang-rename/CMakeLists.txt2
-rw-r--r--clang-tools-extra/unittests/clang-rename/ClangRenameTest.h (renamed from clang-tools-extra/unittests/clang-rename/ClangRenameTests.cpp)55
-rw-r--r--clang-tools-extra/unittests/clang-rename/RenameClassTest.cpp668
10 files changed, 1104 insertions, 50 deletions
diff --git a/clang-tools-extra/clang-rename/CMakeLists.txt b/clang-tools-extra/clang-rename/CMakeLists.txt
index 427164308dd..84e7951e120 100644
--- a/clang-tools-extra/clang-rename/CMakeLists.txt
+++ b/clang-tools-extra/clang-rename/CMakeLists.txt
@@ -13,6 +13,7 @@ add_clang_library(clangRename
clangIndex
clangLex
clangToolingCore
+ clangToolingRefactor
)
add_subdirectory(tool)
diff --git a/clang-tools-extra/clang-rename/RenamingAction.cpp b/clang-tools-extra/clang-rename/RenamingAction.cpp
index 08944552ac4..5956073290b 100644
--- a/clang-tools-extra/clang-rename/RenamingAction.cpp
+++ b/clang-tools-extra/clang-rename/RenamingAction.cpp
@@ -84,10 +84,52 @@ private:
bool PrintLocations;
};
+// A renamer to rename symbols which are identified by a give USRList to
+// new name.
+//
+// FIXME: Merge with the above RenamingASTConsumer.
+class USRSymbolRenamer: public ASTConsumer {
+public:
+ USRSymbolRenamer(const std::vector<std::string> &NewNames,
+ const std::vector<std::vector<std::string>> &USRList,
+ std::map<std::string, tooling::Replacements> &FileToReplaces)
+ : NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) {
+ assert(USRList.size() == NewNames.size());
+ }
+
+ void HandleTranslationUnit(ASTContext &Context) override {
+ for (unsigned I = 0; I < NewNames.size(); ++I) {
+ // FIXME: Apply AtomicChanges directly once the refactoring APIs are
+ // ready.
+ auto AtomicChanges = createRenameAtomicChanges(
+ USRList[I], NewNames[I], Context.getTranslationUnitDecl());
+ for (const auto AtomicChange : AtomicChanges) {
+ for (const auto &Replace : AtomicChange.getReplacements()) {
+ llvm::Error Err = FileToReplaces[Replace.getFilePath()].add(Replace);
+ if (Err) {
+ llvm::errs() << "Renaming failed in " << Replace.getFilePath()
+ << "! " << llvm::toString(std::move(Err)) << "\n";
+ }
+ }
+ }
+ }
+ }
+
+private:
+ const std::vector<std::string> &NewNames;
+ const std::vector<std::vector<std::string>> &USRList;
+ std::map<std::string, tooling::Replacements> &FileToReplaces;
+};
+
std::unique_ptr<ASTConsumer> RenamingAction::newASTConsumer() {
return llvm::make_unique<RenamingASTConsumer>(NewNames, PrevNames, USRList,
FileToReplaces, PrintLocations);
}
+std::unique_ptr<ASTConsumer> QualifiedRenamingAction::newASTConsumer() {
+ return llvm::make_unique<USRSymbolRenamer>(
+ NewNames, USRList, FileToReplaces);
+}
+
} // namespace rename
} // namespace clang
diff --git a/clang-tools-extra/clang-rename/RenamingAction.h b/clang-tools-extra/clang-rename/RenamingAction.h
index 654e13600ca..e8b98b20c50 100644
--- a/clang-tools-extra/clang-rename/RenamingAction.h
+++ b/clang-tools-extra/clang-rename/RenamingAction.h
@@ -42,6 +42,28 @@ private:
bool PrintLocations;
};
+/// Rename all symbols identified by the given USRs.
+class QualifiedRenamingAction {
+public:
+ QualifiedRenamingAction(
+ const std::vector<std::string> &NewNames,
+ const std::vector<std::vector<std::string>> &USRList,
+ std::map<std::string, tooling::Replacements> &FileToReplaces)
+ : NewNames(NewNames), USRList(USRList), FileToReplaces(FileToReplaces) {}
+
+ std::unique_ptr<ASTConsumer> newASTConsumer();
+
+private:
+ /// New symbol names.
+ const std::vector<std::string> &NewNames;
+
+ /// A list of USRs. Each element represents USRs of a symbol being renamed.
+ const std::vector<std::vector<std::string>> &USRList;
+
+ /// A file path to replacements map.
+ std::map<std::string, tooling::Replacements> &FileToReplaces;
+};
+
} // namespace rename
} // namespace clang
diff --git a/clang-tools-extra/clang-rename/USRFinder.cpp b/clang-tools-extra/clang-rename/USRFinder.cpp
index d850af5c03e..901e402ac17 100644
--- a/clang-tools-extra/clang-rename/USRFinder.cpp
+++ b/clang-tools-extra/clang-rename/USRFinder.cpp
@@ -135,7 +135,8 @@ private:
return true;
} else {
// Fully qualified name is used to find the declaration.
- if (Name != Decl->getQualifiedNameAsString())
+ if (Name != Decl->getQualifiedNameAsString() &&
+ Name != "::" + Decl->getQualifiedNameAsString())
return true;
}
Result = Decl;
diff --git a/clang-tools-extra/clang-rename/USRFindingAction.cpp b/clang-tools-extra/clang-rename/USRFindingAction.cpp
index 8fd19667777..6915e4292da 100644
--- a/clang-tools-extra/clang-rename/USRFindingAction.cpp
+++ b/clang-tools-extra/clang-rename/USRFindingAction.cpp
@@ -104,6 +104,10 @@ private:
void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) {
RecordDecl = RecordDecl->getDefinition();
+ // Skip if the CXXRecordDecl doesn't have definition.
+ if (!RecordDecl)
+ return;
+
for (const auto *CtorDecl : RecordDecl->ctors())
USRSet.insert(getUSRForDecl(CtorDecl));
diff --git a/clang-tools-extra/clang-rename/USRLocFinder.cpp b/clang-tools-extra/clang-rename/USRLocFinder.cpp
index 48438b690f1..66fd541fdc2 100644
--- a/clang-tools-extra/clang-rename/USRLocFinder.cpp
+++ b/clang-tools-extra/clang-rename/USRLocFinder.cpp
@@ -22,6 +22,7 @@
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Core/Lookup.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include <cstddef>
@@ -148,6 +149,300 @@ private:
const ASTContext &Context;
};
+SourceLocation StartLocationForType(TypeLoc TL) {
+ // For elaborated types (e.g. `struct a::A`) we want the portion after the
+ // `struct` but including the namespace qualifier, `a::`.
+ if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) {
+ NestedNameSpecifierLoc NestedNameSpecifier =
+ ElaboratedTypeLoc.getQualifierLoc();
+ if (NestedNameSpecifier.getNestedNameSpecifier())
+ return NestedNameSpecifier.getBeginLoc();
+ TL = TL.getNextTypeLoc();
+ }
+ return TL.getLocStart();
+}
+
+SourceLocation EndLocationForType(TypeLoc TL) {
+ // Dig past any namespace or keyword qualifications.
+ while (TL.getTypeLocClass() == TypeLoc::Elaborated ||
+ TL.getTypeLocClass() == TypeLoc::Qualified)
+ TL = TL.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 (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) {
+ return TL.castAs<TemplateSpecializationTypeLoc>()
+ .getLAngleLoc()
+ .getLocWithOffset(-1);
+ }
+ return TL.getEndLoc();
+}
+
+NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) {
+ // Dig past any keyword qualifications.
+ while (TL.getTypeLocClass() == TypeLoc::Qualified)
+ TL = TL.getNextTypeLoc();
+
+ // For elaborated types (e.g. `struct a::A`) we want the portion after the
+ // `struct` but including the namespace qualifier, `a::`.
+ if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>())
+ return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier();
+ return nullptr;
+}
+
+// Find all locations identified by the given USRs for rename.
+//
+// This class will traverse the AST and find every AST node whose USR is in the
+// given USRs' set.
+class RenameLocFinder
+ : public RecursiveASTVisitor<RenameLocFinder> {
+public:
+ RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context)
+ : USRSet(USRs.begin(), USRs.end()), Context(Context) {}
+
+ // A structure records all information of a symbol reference being renamed.
+ // We try to add as few prefix qualifiers as possible.
+ struct RenameInfo {
+ // The begin location of a symbol being renamed.
+ SourceLocation Begin;
+ // The end location of a symbol being renamed.
+ SourceLocation End;
+ // The declaration of a symbol being renamed (can be nullptr).
+ const NamedDecl *FromDecl;
+ // The declaration in which the nested name is contained (can be nullptr).
+ const Decl *Context;
+ // The nested name being replaced (can be nullptr).
+ const NestedNameSpecifier *Specifier;
+ };
+
+ // FIXME: Currently, prefix qualifiers will be added to the renamed symbol
+ // definition (e.g. "class Foo {};" => "class b::Bar {};" when renaming
+ // "a::Foo" to "b::Bar").
+ // For renaming declarations/definitions, prefix qualifiers should be filtered
+ // out.
+ bool VisitNamedDecl(const NamedDecl *Decl) {
+ // UsingDecl has been handled in other place.
+ if (llvm::isa<UsingDecl>(Decl))
+ return true;
+
+ // DestructorDecl has been handled in Typeloc.
+ if (llvm::isa<CXXDestructorDecl>(Decl))
+ return true;
+
+ if (Decl->isImplicit())
+ return true;
+
+ if (isInUSRSet(Decl)) {
+ RenameInfo Info = {Decl->getLocation(), Decl->getLocation(), nullptr,
+ nullptr, nullptr};
+ RenameInfos.push_back(Info);
+ }
+ return true;
+ }
+
+ bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
+ const NamedDecl *Decl = Expr->getFoundDecl();
+ if (isInUSRSet(Decl)) {
+ RenameInfo Info = {Expr->getSourceRange().getBegin(),
+ Expr->getSourceRange().getEnd(), Decl,
+ getClosestAncestorDecl(*Expr), Expr->getQualifier()};
+ RenameInfos.push_back(Info);
+ }
+
+ return true;
+ }
+
+ bool VisitUsingDecl(const UsingDecl *Using) {
+ for (const auto *UsingShadow : Using->shadows()) {
+ if (isInUSRSet(UsingShadow->getTargetDecl())) {
+ UsingDecls.push_back(Using);
+ break;
+ }
+ }
+ return true;
+ }
+
+ bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) {
+ if (!NestedLoc.getNestedNameSpecifier()->getAsType())
+ return true;
+ if (IsTypeAliasWhichWillBeRenamedElsewhere(NestedLoc.getTypeLoc()))
+ return true;
+
+ if (const auto *TargetDecl =
+ getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) {
+ if (isInUSRSet(TargetDecl)) {
+ RenameInfo Info = {NestedLoc.getBeginLoc(),
+ EndLocationForType(NestedLoc.getTypeLoc()),
+ TargetDecl, getClosestAncestorDecl(NestedLoc),
+ NestedLoc.getNestedNameSpecifier()->getPrefix()};
+ RenameInfos.push_back(Info);
+ }
+ }
+ return true;
+ }
+
+ bool VisitTypeLoc(TypeLoc Loc) {
+ if (IsTypeAliasWhichWillBeRenamedElsewhere(Loc))
+ return true;
+
+ auto Parents = Context.getParents(Loc);
+ TypeLoc ParentTypeLoc;
+ if (!Parents.empty()) {
+ // Handle cases of nested name specificier locations.
+ //
+ // The VisitNestedNameSpecifierLoc interface is not impelmented in
+ // RecursiveASTVisitor, we have to handle it explicitly.
+ if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) {
+ VisitNestedNameSpecifierLocations(*NSL);
+ return true;
+ }
+
+ if (const auto *TL = Parents[0].get<TypeLoc>())
+ ParentTypeLoc = *TL;
+ }
+
+ // Handle the outermost TypeLoc which is directly linked to the interesting
+ // declaration and don't handle nested name specifier locations.
+ if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) {
+ if (isInUSRSet(TargetDecl)) {
+ // Only handle the outermost typeLoc.
+ //
+ // For a type like "a::Foo", there will be two typeLocs for it.
+ // One ElaboratedType, the other is RecordType:
+ //
+ // ElaboratedType 0x33b9390 'a::Foo' sugar
+ // `-RecordType 0x338fef0 'class a::Foo'
+ // `-CXXRecord 0x338fe58 'Foo'
+ //
+ // Skip if this is an inner typeLoc.
+ if (!ParentTypeLoc.isNull() &&
+ isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc)))
+ return true;
+ RenameInfo Info = {StartLocationForType(Loc), EndLocationForType(Loc),
+ TargetDecl, getClosestAncestorDecl(Loc),
+ GetNestedNameForType(Loc)};
+ RenameInfos.push_back(Info);
+ return true;
+ }
+ }
+
+ // Handle specific template class specialiation cases.
+ if (const auto *TemplateSpecType =
+ dyn_cast<TemplateSpecializationType>(Loc.getType())) {
+ TypeLoc TargetLoc = Loc;
+ if (!ParentTypeLoc.isNull()) {
+ if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
+ TargetLoc = ParentTypeLoc;
+ }
+
+ if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) {
+ TypeLoc TargetLoc = Loc;
+ // FIXME: Find a better way to handle this case.
+ // For the qualified template class specification type like
+ // "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc
+ // (ElaboratedType) of the TemplateSpecializationType in order to
+ // catch the prefix qualifiers "ns::".
+ if (!ParentTypeLoc.isNull() &&
+ llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
+ TargetLoc = ParentTypeLoc;
+ RenameInfo Info = {
+ StartLocationForType(TargetLoc), EndLocationForType(TargetLoc),
+ TemplateSpecType->getTemplateName().getAsTemplateDecl(),
+ getClosestAncestorDecl(
+ ast_type_traits::DynTypedNode::create(TargetLoc)),
+ GetNestedNameForType(TargetLoc)};
+ RenameInfos.push_back(Info);
+ }
+ }
+ return true;
+ }
+
+ // Returns a list of RenameInfo.
+ const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; }
+
+ // Returns a list of using declarations which are needed to update.
+ const std::vector<const UsingDecl *> &getUsingDecls() const {
+ return UsingDecls;
+ }
+
+private:
+ // FIXME: This method may not be suitable for renaming other types like alias
+ // types. Need to figure out a way to handle it.
+ bool IsTypeAliasWhichWillBeRenamedElsewhere(TypeLoc TL) const {
+ while (!TL.isNull()) {
+ // SubstTemplateTypeParm is the TypeLocation class for a substituted type
+ // inside a template expansion so we ignore these. For example:
+ //
+ // template<typename T> struct S {
+ // T t; // <-- this T becomes a TypeLoc(int) with class
+ // // SubstTemplateTypeParm when S<int> is instantiated
+ // }
+ if (TL.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm)
+ return true;
+
+ // Typedef is the TypeLocation class for a type which is a typedef to the
+ // type we want to replace. We ignore the use of the typedef as we will
+ // replace the definition of it. For example:
+ //
+ // typedef int T;
+ // T a; // <--- This T is a TypeLoc(int) with class Typedef.
+ if (TL.getTypeLocClass() == TypeLoc::Typedef)
+ return true;
+ TL = TL.getNextTypeLoc();
+ }
+ return false;
+ }
+
+ // Get the supported declaration from a given typeLoc. If the declaration type
+ // is not supported, returns nullptr.
+ //
+ // FIXME: support more types, e.g. enum, type alias.
+ const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) {
+ if (const auto *RD = Loc.getType()->getAsCXXRecordDecl())
+ return RD;
+ return nullptr;
+ }
+
+ // Get the closest ancester which is a declaration of a given AST node.
+ template <typename ASTNodeType>
+ const Decl *getClosestAncestorDecl(ASTNodeType Node) {
+ auto Parents = Context.getParents(Node);
+ // FIXME: figure out how to handle it when there are multiple parents.
+ if (Parents.size() != 1)
+ return nullptr;
+ if (ast_type_traits::ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(
+ Parents[0].getNodeKind()))
+ return Parents[0].template get<Decl>();
+ return getClosestAncestorDecl(Parents[0]);
+ }
+
+ // Get the parent typeLoc of a given typeLoc. If there is no such parent,
+ // return nullptr.
+ const TypeLoc *getParentTypeLoc(TypeLoc Loc) const {
+ auto Parents = Context.getParents(Loc);
+ // FIXME: figure out how to handle it when there are multiple parents.
+ if (Parents.size() != 1)
+ return nullptr;
+ return Parents[0].get<TypeLoc>();
+ }
+
+ // Check whether the USR of a given Decl is in the USRSet.
+ bool isInUSRSet(const Decl *Decl) const {
+ auto USR = getUSRForDecl(Decl);
+ if (USR.empty())
+ return false;
+ return llvm::is_contained(USRSet, USR);
+ }
+
+ const std::set<std::string> USRSet;
+ ASTContext &Context;
+ std::vector<RenameInfo> RenameInfos;
+ // Record all interested using declarations which contains the using-shadow
+ // declarations of the symbol declarations being renamed.
+ std::vector<const UsingDecl *> UsingDecls;
+};
+
} // namespace
std::vector<SourceLocation>
@@ -163,5 +458,53 @@ getLocationsOfUSRs(const std::vector<std::string> &USRs, StringRef PrevName,
return Visitor.getLocationsFound();
}
+std::vector<tooling::AtomicChange>
+createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,
+ llvm::StringRef NewName, Decl *TranslationUnitDecl) {
+ RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext());
+ Finder.TraverseDecl(TranslationUnitDecl);
+
+ const SourceManager &SM =
+ TranslationUnitDecl->getASTContext().getSourceManager();
+
+ std::vector<tooling::AtomicChange> AtomicChanges;
+ auto Replace = [&](SourceLocation Start, SourceLocation End,
+ llvm::StringRef Text) {
+ tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start);
+ llvm::Error Err = ReplaceChange.replace(
+ SM, CharSourceRange::getTokenRange(Start, End), Text);
+ if (Err) {
+ llvm::errs() << "Faile to add replacement to AtomicChange: "
+ << llvm::toString(std::move(Err)) << "\n";
+ return;
+ }
+ AtomicChanges.push_back(std::move(ReplaceChange));
+ };
+
+ for (const auto &RenameInfo : Finder.getRenameInfos()) {
+ std::string ReplacedName = NewName.str();
+ if (RenameInfo.FromDecl && RenameInfo.Context) {
+ if (!llvm::isa<clang::TranslationUnitDecl>(
+ RenameInfo.Context->getDeclContext())) {
+ ReplacedName = tooling::replaceNestedName(
+ RenameInfo.Specifier, RenameInfo.Context->getDeclContext(),
+ RenameInfo.FromDecl,
+ NewName.startswith("::") ? NewName.str() : ("::" + NewName).str());
+ }
+ }
+ // If the NewName contains leading "::", add it back.
+ if (NewName.startswith("::") && NewName.substr(2) == ReplacedName)
+ ReplacedName = NewName.str();
+ Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName);
+ }
+
+ // Hanlde using declarations explicitly as "using a::Foo" don't trigger
+ // typeLoc for "a::Foo".
+ for (const auto *Using : Finder.getUsingDecls())
+ Replace(Using->getLocStart(), Using->getLocEnd(), "using " + NewName.str());
+
+ return AtomicChanges;
+}
+
} // namespace rename
} // namespace clang
diff --git a/clang-tools-extra/clang-rename/USRLocFinder.h b/clang-tools-extra/clang-rename/USRLocFinder.h
index 1b5473bd273..c528d41aeb0 100644
--- a/clang-tools-extra/clang-rename/USRLocFinder.h
+++ b/clang-tools-extra/clang-rename/USRLocFinder.h
@@ -18,12 +18,26 @@
#include "clang/AST/AST.h"
#include "llvm/ADT/StringRef.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Refactoring/AtomicChange.h"
#include <string>
#include <vector>
namespace clang {
namespace rename {
+/// Create atomic changes for renaming all symbol references which are
+/// identified by the USRs set to a given new name.
+///
+/// \param USRs: The set containing USRs of a particular old symbol.
+/// \param NewName: The new name to replace old symbol name.
+/// \param TranslationUnitDecl: The translation unit declaration.
+///
+/// \return Atomic changes for renaming.
+std::vector<tooling::AtomicChange>
+createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,
+ llvm::StringRef NewName, Decl *TranslationUnitDecl);
+
// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree!
std::vector<SourceLocation>
getLocationsOfUSRs(const std::vector<std::string> &USRs,
diff --git a/clang-tools-extra/unittests/clang-rename/CMakeLists.txt b/clang-tools-extra/unittests/clang-rename/CMakeLists.txt
index be474f0c2b5..a121fdcdffd 100644
--- a/clang-tools-extra/unittests/clang-rename/CMakeLists.txt
+++ b/clang-tools-extra/unittests/clang-rename/CMakeLists.txt
@@ -12,7 +12,7 @@ include_directories(
include_directories(${CLANG_SOURCE_DIR})
add_extra_unittest(ClangRenameTests
- ClangRenameTests.cpp
+ RenameClassTest.cpp
)
target_link_libraries(ClangRenameTests
diff --git a/clang-tools-extra/unittests/clang-rename/ClangRenameTests.cpp b/clang-tools-extra/unittests/clang-rename/ClangRenameTest.h
index 12054b622e0..fd7f37a1d65 100644
--- a/clang-tools-extra/unittests/clang-rename/ClangRenameTests.cpp
+++ b/clang-tools-extra/unittests/clang-rename/ClangRenameTest.h
@@ -30,19 +30,19 @@
namespace clang {
namespace clang_rename {
-namespace {
+namespace test {
struct Case {
std::string Before;
std::string After;
+ std::string OldName;
+ std::string NewName;
};
class ClangRenameTest : public testing::Test,
public testing::WithParamInterface<Case> {
protected:
- void AppendToHeader(StringRef Code) {
- HeaderContent += Code.str();
- }
+ void AppendToHeader(StringRef Code) { HeaderContent += Code.str(); }
std::string runClangRenameOnCode(llvm::StringRef Code,
llvm::StringRef OldName,
@@ -68,11 +68,10 @@ protected:
const std::vector<std::vector<std::string>> &USRList =
FindingAction.getUSRList();
- const std::vector<std::string> &PrevNames = FindingAction.getUSRSpellings();
std::vector<std::string> NewNames = {NewName};
std::map<std::string, tooling::Replacements> FileToReplacements;
- rename::RenamingAction RenameAction(NewNames, PrevNames, USRList,
- FileToReplacements);
+ rename::QualifiedRenamingAction RenameAction(NewNames, USRList,
+ FileToReplacements);
auto RenameActionFactory = tooling::newFrontendActionFactory(&RenameAction);
if (!tooling::runToolOnCodeWithArgs(
RenameActionFactory->create(), NewCode, {"-std=c++11"}, CCName,
@@ -108,46 +107,6 @@ protected:
std::string CCName = "input.cc";
};
-class RenameClassTest : public ClangRenameTest {
- public:
- RenameClassTest() {
- AppendToHeader("\nclass Foo {};\n");
- }
-};
-
-INSTANTIATE_TEST_CASE_P(
- RenameTests, RenameClassTest,
- testing::ValuesIn(std::vector<Case>({
- {"Foo f;", "Bar f;"},
- {"void f(Foo f) {}", "void f(Bar f) {}"},
- {"void f(Foo *f) {}", "void f(Bar *f) {}"},
- {"Foo f() { return Foo(); }", "Bar f() { return Bar(); }"},
- })));
-
-TEST_P(RenameClassTest, RenameClasses) {
- auto Param = GetParam();
- std::string OldName = "Foo";
- std::string NewName = "Bar";
- std::string Actual = runClangRenameOnCode(Param.Before, OldName, NewName);
- CompareSnippets(Param.After, Actual);
-}
-
-class RenameFunctionTest : public ClangRenameTest {};
-
-INSTANTIATE_TEST_CASE_P(
- RenameTests, RenameFunctionTest,
- testing::ValuesIn(std::vector<Case>({
- {"void func1() {}", "void func2() {}"},
- })));
-
-TEST_P(RenameFunctionTest, RenameFunctions) {
- auto Param = GetParam();
- std::string OldName = "func1";
- std::string NewName = "func2";
- std::string Actual = runClangRenameOnCode(Param.Before, OldName, NewName);
- CompareSnippets(Param.After, Actual);
-}
-
-} // anonymous namespace
+} // namespace test
} // namespace clang_rename
} // namesdpace clang
diff --git a/clang-tools-extra/unittests/clang-rename/RenameClassTest.cpp b/clang-tools-extra/unittests/clang-rename/RenameClassTest.cpp
new file mode 100644
index 00000000000..f6ad962aad8
--- /dev/null
+++ b/clang-tools-extra/unittests/clang-rename/RenameClassTest.cpp
@@ -0,0 +1,668 @@
+//===-- RenameClassTest.cpp - unit tests for renaming classes -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangRenameTest.h"
+
+namespace clang {
+namespace clang_rename {
+namespace test {
+namespace {
+
+class RenameClassTest : public ClangRenameTest {
+public:
+ RenameClassTest() {
+ AppendToHeader(R"(
+ namespace a {
+ class Foo {
+ public:
+ struct Nested {
+ enum NestedEnum {E1, E2};
+ };
+ void func() {}
+ static int Constant;
+ };
+ class Goo {
+ public:
+ struct Nested {
+ enum NestedEnum {E1, E2};
+ };
+ };
+ int Foo::Constant = 1;
+ } // namespace a
+ namespace b {
+ class Foo {};
+ } // namespace b
+
+ #define MACRO(x) x
+
+ template<typename T> class ptr {};
+ )");
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(
+ RenameClassTests, RenameClassTest,
+ testing::ValuesIn(std::vector<Case>({
+ // basic classes
+ {"a::Foo f;", "b::Bar f;"},
+ {"void f(a::Foo f) {}", "void f(b::Bar f) {}"},
+ {"void f(a::Foo *f) {}", "void f(b::Bar *f) {}"},
+ {"a::Foo f() { return a::Foo(); }", "b::Bar f() { return b::Bar(); }"},
+ {"namespace a {a::Foo f() { return Foo(); }}",
+ "namespace a {b::Bar f() { return b::Bar(); }}"},
+ {"void f(const a::Foo& a1) {}", "void f(const b::Bar& a1) {}"},
+ {"void f(const a::Foo* a1) {}", "void f(const b::Bar* a1) {}"},
+ {"namespace a { void f(Foo a1) {} }",
+ "namespace a { void f(b::Bar a1) {} }"},
+ {"void f(MACRO(a::Foo) a1) {}", "void f(MACRO(b::Bar) a1) {}"},
+ {"void f(MACRO(a::Foo a1)) {}", "void f(MACRO(b::Bar a1)) {}"},
+ {"a::Foo::Nested ns;", "b::Bar::Nested ns;"},
+ {"auto t = a::Foo::Constant;", "auto t = b::Bar::Constant;"},
+ {"a::Foo::Nested ns;", "a::Foo::Nested2 ns;", "a::Foo::Nested",
+ "a::Foo::Nested2"},
+
+ // use namespace and typedefs
+ {"using a::Foo; Foo gA;", "using b::Bar; b::Bar gA;"},
+ {"using a::Foo; void f(Foo gA) {}", "using b::Bar; void f(Bar gA) {}"},
+ {"using a::Foo; namespace x { Foo gA; }",
+ "using b::Bar; namespace x { Bar gA; }"},
+ {"struct S { using T = a::Foo; T a_; };",
+ "struct S { using T = b::Bar; T a_; };"},
+ {"using T = a::Foo; T gA;", "using T = b::Bar; T gA;"},
+ {"typedef a::Foo T; T gA;", "typedef b::Bar T; T gA;"},
+ {"typedef MACRO(a::Foo) T; T gA;", "typedef MACRO(b::Bar) T; T gA;"},
+
+ // struct members and other oddities
+ {"struct S : public a::Foo {};", "struct S : public b::Bar {};"},
+ {"struct F { void f(a::Foo a1) {} };",
+ "struct F { void f(b::Bar a1) {} };"},
+ {"struct F { a::Foo a_; };", "struct F { b::Bar a_; };"},
+ {"struct F { ptr<a::Foo> a_; };", "struct F { ptr<b::Bar> a_; };"},
+
+ {"void f() { a::Foo::Nested ne; }", "void f() { b::Bar::Nested ne; }"},
+ {"void f() { a::Goo::Nested ne; }", "void f() { a::Goo::Nested ne; }"},
+ {"void f() { a::Foo::Nested::NestedEnum e; }",
+ "void f() { b::Bar::Nested::NestedEnum e; }"},
+ {"void f() { auto e = a::Foo::Nested::NestedEnum::E1; }",
+ "void f() { auto e = b::Bar::Nested::NestedEnum::E1; }"},
+ {"void f() { auto e = a::Foo::Nested::E1; }",
+ "void f() { auto e = b::Bar::Nested::E1; }"},
+
+ // templates
+ {"template <typename T> struct Foo { T t; };\n"
+ "void f() { Foo<a::Foo> foo; }",
+ "template <typename T> struct Foo { T t; };\n"
+ "void f() { Foo<b::Bar> foo; }"},
+ {"template <typename T> struct Foo { a::Foo a; };",
+ "template <typename T> struct Foo { b::Bar a; };"},
+ {"template <typename T> void f(T t) {}\n"
+ "void g() { f<a::Foo>(a::Foo()); }",
+ "template <typename T> void f(T t) {}\n"
+ "void g() { f<b::Bar>(b::Bar()); }"},
+ {"template <typename T> int f() { return 1; }\n"
+ "template <> int f<a::Foo>() { return 2; }\n"
+ "int g() { return f<a::Foo>(); }",
+ "template <typename T> int f() { return 1; }\n"
+ "template <> int f<b::Bar>() { return 2; }\n"
+ "int g() { return f<b::Bar>(); }"},
+ {"struct Foo { template <typename T> T foo(); };\n"
+ "void g() { Foo f; auto a = f.template foo<a::Foo>(); }",
+ "struct Foo { template <typename T> T foo(); };\n"
+ "void g() { Foo f; auto a = f.template foo<b::Bar>(); }"},
+
+ // The following two templates are distilled from regressions found in
+ // unique_ptr<> and type_traits.h
+ {"template <typename T> struct outer {\n"
+ " typedef T type;\n"
+ " type Baz();\n"
+ " };\n"
+ " outer<a::Foo> g_A;",
+ "template <typename T> struct outer {\n"
+ " typedef T type;\n"
+ " type Baz();\n"
+ " };\n"
+ " outer<b::Bar> g_A;"},
+ {"template <typename T> struct nested { typedef T type; };\n"
+ "template <typename T> struct outer { typename nested<T>::type Foo(); "
+ "};\n"
+ "outer<a::Foo> g_A;",
+ "template <typename T> struct nested { typedef T type; };\n"
+ "template <typename T> struct outer { typename nested<T>::type Foo(); "
+ "};\n"
+ "outer<b::Bar> g_A;"},
+
+ // macros
+ {"#define FOO(T, t) T t\n"
+ "void f() { FOO(a::Foo, a1); FOO(a::Foo, a2); }",
+ "#define FOO(T, t) T t\n"
+ "void f() { FOO(b::Bar, a1); FOO(b::Bar, a2); }"},
+ {"#define FOO(n) a::Foo n\n"
+ " void f() { FOO(a1); FOO(a2); }",
+ "#define FOO(n) b::Bar n\n"
+ " void f() { FOO(a1); FOO(a2); }"},
+
+ // Pointer to member functions
+ {"auto gA = &a::Foo::func;", "auto gA = &b::Bar::func;"},
+ {"using a::Foo; auto gA = &Foo::func;",
+ "using b::Bar; auto gA = &b::Bar::func;"},
+ {"using a::Foo; namespace x { auto gA = &Foo::func; }",
+ "using b::Bar; namespace x { auto gA = &Bar::func; }"},
+ {"typedef a::Foo T; auto gA = &T::func;",
+ "typedef b::Bar T; auto gA = &T::func;"},
+ {"auto gA = &MACRO(a::Foo)::func;", "auto gA = &MACRO(b::Bar)::func;"},
+
+ // Short match inside a namespace
+ {"namespace a { void f(Foo a1) {} }",
+ "namespace a { void f(b::Bar a1) {} }"},
+
+ // Correct match.
+ {"using a::Foo; struct F { ptr<Foo> a_; };",
+ "using b::Bar; struct F { ptr<Bar> a_; };"},
+
+ // avoid false positives
+ {"void f(b::Foo a) {}", "void f(b::Foo a) {}"},
+ {"namespace b { void f(Foo a) {} }",
+ "namespace b { void f(Foo a) {} }"},
+
+ // friends, everyone needs friends.
+ {"class Foo { int i; friend class a::Foo; };",
+ "class Foo { int i; friend class b::Bar; };"},
+ })));
+
+TEST_P(RenameClassTest, RenameClasses) {
+ auto Param = GetParam();
+ std::string OldName = Param.OldName.empty() ? "a::Foo" : Param.OldName;
+ std::string NewName = Param.NewName.empty() ? "b::Bar" : Param.NewName;
+ std::string Actual = runClangRenameOnCode(Param.Before, OldName, NewName);
+ CompareSnippets(Param.After, Actual);
+}
+
+class NamespaceDetectionTest : public ClangRenameTest {
+protected:
+ NamespaceDetectionTest() {
+ AppendToHeader(R"(
+ class Old {};
+ namespace o1 {
+ class Old {};
+ namespace o2 {
+ class Old {};
+ namespace o3 {
+ class Old {};
+ } // namespace o3
+ } // namespace o2
+ } // namespace o1
+ )");
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(
+ RenameClassTest, NamespaceDetectionTest,
+ ::testing::ValuesIn(std::vector<Case>({
+ // Test old and new namespace overlap.
+ {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+ "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+ "o1::o2::o3::Old", "o1::o2::o3::New"},
+ {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+ "namespace o1 { namespace o2 { namespace o3 { n3::New moo; } } }",
+ "o1::o2::o3::Old", "o1::o2::n3::New"},
+ {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+ "namespace o1 { namespace o2 { namespace o3 { n2::n3::New moo; } } }",
+ "o1::o2::o3::Old", "o1::n2::n3::New"},
+ {"namespace o1 { namespace o2 { Old moo; } }",
+ "namespace o1 { namespace o2 { New moo; } }", "::o1::o2::Old",
+ "::o1::o2::New"},
+ {"namespace o1 { namespace o2 { Old moo; } }",
+ "namespace o1 { namespace o2 { n2::New moo; } }", "::o1::o2::Old",
+ "::o1::n2::New"},
+ {"namespace o1 { namespace o2 { Old moo; } }",
+ "namespace o1 { namespace o2 { ::n1::n2::New moo; } }",
+ "::o1::o2::Old", "::n1::n2::New"},
+ {"namespace o1 { namespace o2 { Old moo; } }",
+ "namespace o1 { namespace o2 { n1::n2::New moo; } }", "::o1::o2::Old",
+ "n1::n2::New"},
+
+ // Test old and new namespace with differing depths.
+ {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+ "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+ "o1::o2::o3::Old", "::o1::New"},
+ {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+ "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+ "o1::o2::o3::Old", "::o1::o2::New"},
+ {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+ "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+ "o1::o2::o3::Old", "o1::New"},
+ {"namespace o1 { namespace o2 { namespace o3 { Old moo; } } }",
+ "namespace o1 { namespace o2 { namespace o3 { New moo; } } }",
+ "o1::o2::o3::Old", "o1::o2::New"},
+ {"Old moo;", "o1::New moo;", "::Old", "o1::New"},
+ {"Old moo;", "o1::New moo;", "Old", "o1::New"},
+ {"namespace o1 { ::Old moo; }", "namespace o1 { New moo; }", "Old",
+ "o1::New"},
+ {"namespace o1 { namespace o2 { Old moo; } }",
+ "namespace o1 { namespace o2 { ::New moo; } }", "::o1::o2::Old",
+ "::New"},
+ {"namespace o1 { namespace o2 { Old moo; } }",
+ "namespace o1 { namespace o2 { New moo; } }", "::o1::o2::Old", "New"},
+
+ // Test moving into the new namespace at different levels.
+ {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+ "namespace n1 { namespace n2 { New moo; } }", "::o1::o2::Old",
+ "::n1::n2::New"},
+ {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+ "namespace n1 { namespace n2 { New moo; } }", "::o1::o2::Old",
+ "n1::n2::New"},
+ {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+ "namespace n1 { namespace n2 { o2::New moo; } }", "::o1::o2::Old",
+ "::n1::o2::New"},
+ {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+ "namespace n1 { namespace n2 { o2::New moo; } }", "::o1::o2::Old",
+ "n1::o2::New"},
+ {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+ "namespace n1 { namespace n2 { ::o1::o2::New moo; } }",
+ "::o1::o2::Old", "::o1::o2::New"},
+ {"namespace n1 { namespace n2 { o1::o2::Old moo; } }",
+ "namespace n1 { namespace n2 { o1::o2::New moo; } }", "::o1::o2::Old",
+ "o1::o2::New"},
+
+ // Test friends declarations.
+ {"class Foo { friend class o1::Old; };",
+ "class Foo { friend class o1::New; };", "o1::Old", "o1::New"},
+ {"class Foo { int i; friend class o1::Old; };",
+ "class Foo { int i; friend class ::o1::New; };", "::o1::Old",
+ "::o1::New"},
+ {"namespace o1 { class Foo { int i; friend class Old; }; }",
+ "namespace o1 { class Foo { int i; friend class New; }; }", "o1::Old",
+ "o1::New"},
+ {"namespace o1 { class Foo { int i; friend class Old; }; }",
+ "namespace o1 { class Foo { int i; friend class New; }; }",
+ "::o1::Old", "::o1::New"},
+ })));
+
+TEST_P(NamespaceDetectionTest, RenameClasses) {
+ auto Param = GetParam();
+ std::string Actual =
+ runClangRenameOnCode(Param.Before, Param.OldName, Param.NewName);
+ CompareSnippets(Param.After, Actual);
+}
+
+class TemplatedClassRenameTest : public ClangRenameTest {
+protected:
+ TemplatedClassRenameTest() {
+ AppendToHeader(R"(
+ template <typename T> struct Old {
+ T t_;
+ T f() { return T(); };
+ static T s(T t) { return t; }
+ };
+ namespace ns {
+ template <typename T> struct Old {
+ T t_;
+ T f() { return T(); };
+ static T s(T t) { return t; }
+ };
+ } // namespace ns
+
+ namespace o1 {
+ namespace o2 {
+ namespace o3 {
+ template <typename T> struct Old {
+ T t_;
+ T f() { return T(); };
+ static T s(T t) { return t; }
+ };
+ } // namespace o3
+ } // namespace o2
+ } // namespace o1
+ )");
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(
+ RenameClassTests, TemplatedClassRenameTest,
+ ::testing::ValuesIn(std::vector<Case>({
+ {"Old<int> gI; Old<bool> gB;", "New<int> gI; New<bool> gB;", "Old",
+ "New"},
+ {"ns::Old<int> gI; ns::Old<bool> gB;",
+ "ns::New<int> gI; ns::New<bool> gB;", "ns::Old", "ns::New"},
+ {"auto gI = &Old<int>::f; auto gB = &Old<bool>::f;",
+ "auto gI = &New<int>::f; auto gB = &New<bool>::f;", "Old", "New"},
+ {"auto gI = &ns::Old<int>::f;", "auto gI = &ns::New<int>::f;",
+ "ns::Old", "ns::New"},
+
+ {"int gI = Old<int>::s(0); bool gB = Old<bool>::s(false);",
+ "int gI = New<int>::s(0); bool gB = New<bool>::s(false);", "Old",
+ "New"},
+ {"int gI = ns::Old<int>::s(0); bool gB = ns::Old<bool>::s(false);",
+ "int gI = ns::New<int>::s(0); bool gB = ns::New<bool>::s(false);",
+ "ns::Old", "ns::New"},
+
+ {"struct S { Old<int*> o_; };", "struct S { New<int*> o_; };", "Old",
+ "New"},
+ {"struct S { ns::Old<int*> o_; };", "struct S { ns::New<int*> o_; };",
+ "ns::Old", "ns::New"},
+
+ {"auto a = reinterpret_cast<Old<int>*>(new Old<int>);",
+ "auto a = reinterpret_cast<New<int>*>(new New<int>);", "Old", "New"},
+ {"auto a = reinterpret_cast<ns::Old<int>*>(new ns::Old<int>);",
+ "auto a = reinterpret_cast<ns::New<int>*>(new ns::New<int>);",
+ "ns::Old", "ns::New"},
+ {"auto a = reinterpret_cast<const Old<int>*>(new Old<int>);",
+ "auto a = reinterpret_cast<const New<int>*>(new New<int>);", "Old",
+ "New"},
+ {"auto a = reinterpret_cast<const ns::Old<int>*>(new ns::Old<int>);",
+ "auto a = reinterpret_cast<const ns::New<int>*>(new ns::New<int>);",
+ "ns::Old", "ns::New"},
+
+ {"Old<bool>& foo();", "New<bool>& foo();", "Old", "New"},
+ {"ns::Old<bool>& foo();", "ns::New<bool>& foo();", "ns::Old",
+ "ns::New"},
+ {"o1::o2::o3::Old<bool>& foo();", "o1::o2::o3::New<bool>& foo();",
+ "o1::o2::o3::Old", "o1::o2::o3::New"},
+ {"namespace ns { Old<bool>& foo(); }",
+ "namespace ns { New<bool>& foo(); }", "ns::Old", "ns::New"},
+ {"const Old<bool>& foo();", "const New<bool>& foo();", "Old", "New"},
+ {"const ns::Old<bool>& foo();", "const ns::New<bool>& foo();",
+ "ns::Old", "ns::New"},
+
+ // FIXME: figure out why this only works when Moo gets
+ // specialized at some point.
+ {"template <typename T> struct Moo { Old<T> o_; }; Moo<int> m;",
+ "template <typename T> struct Moo { New<T> o_; }; Moo<int> m;", "Old",
+ "New"},
+ {"template <typename T> struct Moo { ns::Old<T> o_; }; Moo<int> m;",
+ "template <typename T> struct Moo { ns::New<T> o_; }; Moo<int> m;",
+ "ns::Old", "ns::New"},
+ })));
+
+TEST_P(TemplatedClassRenameTest, RenameTemplateClasses) {
+ auto Param = GetParam();
+ std::string Actual =
+ runClangRenameOnCode(Param.Before, Param.OldName, Param.NewName);
+ CompareSnippets(Param.After, Actual);
+}
+
+TEST_F(ClangRenameTest, RenameClassWithOutOfLineMembers) {
+ std::string Before = R"(
+ class Old {
+ public:
+ Old();
+ ~Old();
+
+ Old* next();
+
+ private:
+ Old* next_;
+ };
+
+ Old::Old() {}
+ Old::~Old() {}
+ Old* Old::next() { return next_; }
+ )";
+ std::string Expected = R"(
+ class New {
+ public:
+ New();
+ ~New();
+
+ New* next();
+
+ private:
+ New* next_;
+ };
+
+ New::New() {}
+ New::~New() {}
+ New* New::next() { return next_; }
+ )";
+ std::string After = runClangRenameOnCode(Before, "Old", "New");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(ClangRenameTest, RenameClassWithInlineMembers) {
+ std::string Before = R"(
+ class Old {
+ public:
+ Old() {}
+ ~Old() {}
+
+ Old* next() { return next_; }
+
+ private:
+ Old* next_;
+ };
+ )";
+ std::string Expected = R"(
+ class New {
+ public:
+ New() {}
+ ~New() {}
+
+ New* next() { return next_; }
+
+ private:
+ New* next_;
+ };
+ )";
+ std::string After = runClangRenameOnCode(Before, "Old", "New");
+ CompareSnippets(Expected, After);
+}
+
+// FIXME: no prefix qualifiers being added to the class definition and
+// constructor.
+TEST_F(ClangRenameTest, RenameClassWithNamespaceWithInlineMembers) {
+ std::string Before = R"(
+ namespace ns {
+ class Old {
+ public:
+ Old() {}
+ ~Old() {}
+
+ Old* next() { return next_; }
+
+ private:
+ Old* next_;
+ };
+ } // namespace ns
+ )";
+ std::string Expected = R"(
+ namespace ns {
+ class ns::New {
+ public:
+ ns::New() {}
+ ~New() {}
+
+ New* next() { return next_; }
+
+ private:
+ New* next_;
+ };
+ } // namespace ns
+ )";
+ std::string After = runClangRenameOnCode(Before, "ns::Old", "ns::New");
+ CompareSnippets(Expected, After);
+}
+
+// FIXME: no prefix qualifiers being added to the class definition and
+// constructor.
+TEST_F(ClangRenameTest, RenameClassWithNamespaceWithOutOfInlineMembers) {
+ std::string Before = R"(
+ namespace ns {
+ class Old {
+ public:
+ Old();
+ ~Old();
+
+ Old* next();
+
+ private:
+ Old* next_;
+ };
+
+ Old::Old() {}
+ Old::~Old() {}
+ Old* Old::next() { return next_; }
+ } // namespace ns
+ )";
+ std::string Expected = R"(
+ namespace ns {
+ class ns::New {
+ public:
+ ns::New();
+ ~New();
+
+ New* next();
+
+ private:
+ New* next_;
+ };
+
+ New::ns::New() {}
+ New::~New() {}
+ New* New::next() { return next_; }
+ } // namespace ns
+ )";
+ std::string After = runClangRenameOnCode(Before, "ns::Old", "ns::New");
+ CompareSnippets(Expected, After);
+}
+
+// FIXME: no prefix qualifiers being added to the definition.
+TEST_F(ClangRenameTest, RenameClassInInheritedConstructor) {
+ // `using Base::Base;` will generate an implicit constructor containing usage
+ // of `::ns::Old` which should not be matched.
+ std::string Before = R"(
+ namespace ns {
+ class Old {
+ int x;
+ };
+ class Base {
+ protected:
+ Old *moo_;
+ public:
+ Base(Old *moo) : moo_(moo) {}
+ };
+ class Derived : public Base {
+ public:
+ using Base::Base;
+ };
+ } // namespace ns
+ int main() {
+ ::ns::Old foo;
+ ::ns::Derived d(&foo);
+ return 0;
+ })";
+ std::string Expected = R"(
+ namespace ns {
+ class ns::New {
+ int x;
+ };
+ class Base {
+ protected:
+ New *moo_;
+ public:
+ Base(New *moo) : moo_(moo) {}
+ };
+ class Derived : public Base {
+ public:
+ using Base::Base;
+ };
+ } // namespace ns
+ int main() {
+ ::ns::New foo;
+ ::ns::Derived d(&foo);
+ return 0;
+ })";
+ std::string After = runClangRenameOnCode(Before, "ns::Old", "ns::New");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(ClangRenameTest, DontRenameReferencesInImplicitFunction) {
+ std::string Before = R"(
+ namespace ns {
+ class Old {
+ };
+ } // namespace ns
+ struct S {
+ int y;
+ ns::Old old;
+ };
+ void f() {
+ S s1, s2, s3;
+ // This causes an implicit assignment operator to be created.
+ s1 = s2 = s3;
+ }
+ )";
+ std::string Expected = R"(
+ namespace ns {
+ class ::new_ns::New {
+ };
+ } // namespace ns
+ struct S {
+ int y;
+ ::new_ns::New old;
+ };
+ void f() {
+ S s1, s2, s3;
+ // This causes an implicit assignment operator to be created.
+ s1 = s2 = s3;
+ }
+ )";
+ std::string After = runClangRenameOnCode(Before, "ns::Old", "::new_ns::New");
+ CompareSnippets(Expected, After);
+}
+
+// FIXME: no prefix qualifiers being adding to the definition.
+TEST_F(ClangRenameTest, ReferencesInLambdaFunctionParameters) {
+ std::string Before = R"(
+ template <class T>
+ class function;
+ template <class R, class... ArgTypes>
+ class function<R(ArgTypes...)> {
+ public:
+ template <typename Functor>
+ function(Functor f) {}
+
+ function() {}
+
+ R operator()(ArgTypes...) const {}
+ };
+
+ namespace ns {
+ class Old {};
+ void f() {
+ function<void(Old)> func;
+ }
+ } // namespace ns)";
+ std::string Expected = R"(
+ template <class T>
+ class function;
+ template <class R, class... ArgTypes>
+ class function<R(ArgTypes...)> {
+ public:
+ template <typename Functor>
+ function(Functor f) {}
+
+ function() {}
+
+ R operator()(ArgTypes...) const {}
+ };
+
+ namespace ns {
+ class ::new_ns::New {};
+ void f() {
+ function<void(::new_ns::New)> func;
+ }
+ } // namespace ns)";
+ std::string After = runClangRenameOnCode(Before, "ns::Old", "::new_ns::New");
+ CompareSnippets(Expected, After);
+}
+
+} // anonymous namespace
+} // namespace test
+} // namespace clang_rename
+} // namesdpace clang
OpenPOWER on IntegriCloud