summaryrefslogtreecommitdiffstats
path: root/clang
diff options
context:
space:
mode:
Diffstat (limited to 'clang')
-rw-r--r--clang/include/clang/Tooling/Refactoring/Rename/RenamingAction.h70
-rw-r--r--clang/include/clang/Tooling/Refactoring/Rename/USRFinder.h84
-rw-r--r--clang/include/clang/Tooling/Refactoring/Rename/USRFindingAction.h54
-rw-r--r--clang/include/clang/Tooling/Refactoring/Rename/USRLocFinder.h49
-rw-r--r--clang/include/clang/module.modulemap3
-rw-r--r--clang/lib/Tooling/Refactoring/CMakeLists.txt8
-rw-r--r--clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp134
-rw-r--r--clang/lib/Tooling/Refactoring/Rename/USRFinder.cpp213
-rw-r--r--clang/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp236
-rw-r--r--clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp509
-rw-r--r--clang/test/CMakeLists.txt1
-rw-r--r--clang/test/clang-rename/ClassAsTemplateArgument.cpp21
-rw-r--r--clang/test/clang-rename/ClassFindByName.cpp10
-rw-r--r--clang/test/clang-rename/ClassReplacements.cpp11
-rw-r--r--clang/test/clang-rename/ClassSimpleRenaming.cpp14
-rw-r--r--clang/test/clang-rename/ClassTestMulti.cpp11
-rw-r--r--clang/test/clang-rename/ClassTestMultiByName.cpp8
-rw-r--r--clang/test/clang-rename/ComplexFunctionOverride.cpp47
-rw-r--r--clang/test/clang-rename/ComplicatedClassType.cpp63
-rw-r--r--clang/test/clang-rename/Ctor.cpp14
-rw-r--r--clang/test/clang-rename/CtorInitializer.cpp17
-rw-r--r--clang/test/clang-rename/DeclRefExpr.cpp24
-rw-r--r--clang/test/clang-rename/Field.cpp15
-rw-r--r--clang/test/clang-rename/FunctionMacro.cpp20
-rw-r--r--clang/test/clang-rename/FunctionOverride.cpp13
-rw-r--r--clang/test/clang-rename/FunctionWithClassFindByName.cpp12
-rw-r--r--clang/test/clang-rename/IncludeHeaderWithSymbol.cpp10
-rw-r--r--clang/test/clang-rename/Inputs/HeaderWithSymbol.h1
-rw-r--r--clang/test/clang-rename/Inputs/OffsetToNewName.yaml6
-rw-r--r--clang/test/clang-rename/Inputs/QualifiedNameToNewName.yaml6
-rw-r--r--clang/test/clang-rename/InvalidNewName.cpp2
-rw-r--r--clang/test/clang-rename/InvalidOffset.cpp9
-rw-r--r--clang/test/clang-rename/InvalidQualifiedName.cpp4
-rw-r--r--clang/test/clang-rename/MemberExprMacro.cpp22
-rw-r--r--clang/test/clang-rename/Namespace.cpp13
-rw-r--r--clang/test/clang-rename/NoNewName.cpp4
-rw-r--r--clang/test/clang-rename/TemplateClassInstantiation.cpp42
-rw-r--r--clang/test/clang-rename/TemplateTypename.cpp24
-rw-r--r--clang/test/clang-rename/TemplatedClassFunction.cpp22
-rw-r--r--clang/test/clang-rename/UserDefinedConversion.cpp26
-rw-r--r--clang/test/clang-rename/Variable.cpp33
-rw-r--r--clang/test/clang-rename/VariableMacro.cpp21
-rw-r--r--clang/test/clang-rename/YAMLInput.cpp10
-rw-r--r--clang/tools/CMakeLists.txt2
-rw-r--r--clang/tools/clang-rename/CMakeLists.txt19
-rw-r--r--clang/tools/clang-rename/ClangRename.cpp240
-rw-r--r--clang/tools/clang-rename/clang-rename.el79
-rw-r--r--clang/tools/clang-rename/clang-rename.py61
-rw-r--r--clang/unittests/CMakeLists.txt1
-rw-r--r--clang/unittests/Rename/CMakeLists.txt22
-rw-r--r--clang/unittests/Rename/ClangRenameTest.h112
-rw-r--r--clang/unittests/Rename/RenameClassTest.cpp684
52 files changed, 3135 insertions, 1 deletions
diff --git a/clang/include/clang/Tooling/Refactoring/Rename/RenamingAction.h b/clang/include/clang/Tooling/Refactoring/Rename/RenamingAction.h
new file mode 100644
index 00000000000..099eaca6c42
--- /dev/null
+++ b/clang/include/clang/Tooling/Refactoring/Rename/RenamingAction.h
@@ -0,0 +1,70 @@
+//===--- RenamingAction.h - Clang refactoring library ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides an action to rename every symbol at a point.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_RENAME_RENAMING_ACTION_H
+#define LLVM_CLANG_TOOLING_REFACTOR_RENAME_RENAMING_ACTION_H
+
+#include "clang/Tooling/Refactoring.h"
+
+namespace clang {
+class ASTConsumer;
+class CompilerInstance;
+
+namespace tooling {
+
+class RenamingAction {
+public:
+ RenamingAction(const std::vector<std::string> &NewNames,
+ const std::vector<std::string> &PrevNames,
+ const std::vector<std::vector<std::string>> &USRList,
+ std::map<std::string, tooling::Replacements> &FileToReplaces,
+ bool PrintLocations = false)
+ : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
+ FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
+
+ std::unique_ptr<ASTConsumer> newASTConsumer();
+
+private:
+ const std::vector<std::string> &NewNames, &PrevNames;
+ const std::vector<std::vector<std::string>> &USRList;
+ std::map<std::string, tooling::Replacements> &FileToReplaces;
+ 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;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAME_RENAMING_ACTION_H
diff --git a/clang/include/clang/Tooling/Refactoring/Rename/USRFinder.h b/clang/include/clang/Tooling/Refactoring/Rename/USRFinder.h
new file mode 100644
index 00000000000..28d541af43c
--- /dev/null
+++ b/clang/include/clang/Tooling/Refactoring/Rename/USRFinder.h
@@ -0,0 +1,84 @@
+//===--- USRFinder.h - Clang refactoring library --------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Methods for determining the USR of a symbol at a location in source
+/// code.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_RENAME_USR_FINDER_H
+#define LLVM_CLANG_TOOLING_REFACTOR_RENAME_USR_FINDER_H
+
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <string>
+#include <vector>
+
+using namespace llvm;
+using namespace clang::ast_matchers;
+
+namespace clang {
+
+class ASTContext;
+class Decl;
+class SourceLocation;
+class NamedDecl;
+
+namespace tooling {
+
+// Given an AST context and a point, returns a NamedDecl identifying the symbol
+// at the point. Returns null if nothing is found at the point.
+const NamedDecl *getNamedDeclAt(const ASTContext &Context,
+ const SourceLocation Point);
+
+// Given an AST context and a fully qualified name, returns a NamedDecl
+// identifying the symbol with a matching name. Returns null if nothing is
+// found for the name.
+const NamedDecl *getNamedDeclFor(const ASTContext &Context,
+ const std::string &Name);
+
+// Converts a Decl into a USR.
+std::string getUSRForDecl(const Decl *Decl);
+
+// FIXME: Implement RecursiveASTVisitor<T>::VisitNestedNameSpecifier instead.
+class NestedNameSpecifierLocFinder : public MatchFinder::MatchCallback {
+public:
+ explicit NestedNameSpecifierLocFinder(ASTContext &Context)
+ : Context(Context) {}
+
+ std::vector<NestedNameSpecifierLoc> getNestedNameSpecifierLocations() {
+ addMatchers();
+ Finder.matchAST(Context);
+ return Locations;
+ }
+
+private:
+ void addMatchers() {
+ const auto NestedNameSpecifierLocMatcher =
+ nestedNameSpecifierLoc().bind("nestedNameSpecifierLoc");
+ Finder.addMatcher(NestedNameSpecifierLocMatcher, this);
+ }
+
+ void run(const MatchFinder::MatchResult &Result) override {
+ const auto *NNS = Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
+ "nestedNameSpecifierLoc");
+ Locations.push_back(*NNS);
+ }
+
+ ASTContext &Context;
+ std::vector<NestedNameSpecifierLoc> Locations;
+ MatchFinder Finder;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAME_USR_FINDER_H
diff --git a/clang/include/clang/Tooling/Refactoring/Rename/USRFindingAction.h b/clang/include/clang/Tooling/Refactoring/Rename/USRFindingAction.h
new file mode 100644
index 00000000000..8aafee95bc0
--- /dev/null
+++ b/clang/include/clang/Tooling/Refactoring/Rename/USRFindingAction.h
@@ -0,0 +1,54 @@
+//===--- USRFindingAction.h - Clang refactoring library -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides an action to find all relevant USRs at a point.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_RENAME_USR_FINDING_ACTION_H
+#define LLVM_CLANG_TOOLING_REFACTOR_RENAME_USR_FINDING_ACTION_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
+
+#include <string>
+#include <vector>
+
+namespace clang {
+class ASTConsumer;
+class CompilerInstance;
+class NamedDecl;
+
+namespace tooling {
+
+struct USRFindingAction {
+ USRFindingAction(ArrayRef<unsigned> SymbolOffsets,
+ ArrayRef<std::string> QualifiedNames, bool Force)
+ : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
+ ErrorOccurred(false), Force(Force) {}
+ std::unique_ptr<ASTConsumer> newASTConsumer();
+
+ ArrayRef<std::string> getUSRSpellings() { return SpellingNames; }
+ ArrayRef<std::vector<std::string>> getUSRList() { return USRList; }
+ bool errorOccurred() { return ErrorOccurred; }
+
+private:
+ std::vector<unsigned> SymbolOffsets;
+ std::vector<std::string> QualifiedNames;
+ std::vector<std::string> SpellingNames;
+ std::vector<std::vector<std::string>> USRList;
+ bool ErrorOccurred;
+ bool Force;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAME_USR_FINDING_ACTION_H
diff --git a/clang/include/clang/Tooling/Refactoring/Rename/USRLocFinder.h b/clang/include/clang/Tooling/Refactoring/Rename/USRLocFinder.h
new file mode 100644
index 00000000000..733ea1a6ac9
--- /dev/null
+++ b/clang/include/clang/Tooling/Refactoring/Rename/USRLocFinder.h
@@ -0,0 +1,49 @@
+//===--- USRLocFinder.h - Clang refactoring library -----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides functionality for finding all instances of a USR in a given
+/// AST.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_RENAME_USR_LOC_FINDER_H
+#define LLVM_CLANG_TOOLING_REFACTOR_RENAME_USR_LOC_FINDER_H
+
+#include "clang/AST/AST.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Refactoring/AtomicChange.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace tooling {
+
+/// 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,
+ llvm::StringRef PrevName, Decl *Decl);
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAME_USR_LOC_FINDER_H
diff --git a/clang/include/clang/module.modulemap b/clang/include/clang/module.modulemap
index 282c567c6e5..f7e338d9339 100644
--- a/clang/include/clang/module.modulemap
+++ b/clang/include/clang/module.modulemap
@@ -133,9 +133,10 @@ module Clang_StaticAnalyzer_Frontend {
module Clang_Tooling {
requires cplusplus umbrella "Tooling" module * { export * }
- // FIXME: Exclude this header to avoid pulling all of the AST matchers
+ // FIXME: Exclude these headers to avoid pulling all of the AST matchers
// library into clang-format. Due to inline key functions in the headers,
// importing the AST matchers library gives a link dependency on the AST
// matchers (and thus the AST), which clang-format should not have.
exclude header "Tooling/RefactoringCallbacks.h"
+ exclude header "Tooling/Refactoring/Rename/USRFinder.h"
}
diff --git a/clang/lib/Tooling/Refactoring/CMakeLists.txt b/clang/lib/Tooling/Refactoring/CMakeLists.txt
index b2f9b4f4c0c..288582fc1b6 100644
--- a/clang/lib/Tooling/Refactoring/CMakeLists.txt
+++ b/clang/lib/Tooling/Refactoring/CMakeLists.txt
@@ -5,8 +5,16 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangToolingRefactor
AtomicChange.cpp
+ Rename/RenamingAction.cpp
+ Rename/USRFinder.cpp
+ Rename/USRFindingAction.cpp
+ Rename/USRLocFinder.cpp
LINK_LIBS
+ clangAST
+ clangASTMatchers
clangBasic
+ clangIndex
+ clangLex
clangToolingCore
)
diff --git a/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp b/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
new file mode 100644
index 00000000000..de6aba944a4
--- /dev/null
+++ b/clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp
@@ -0,0 +1,134 @@
+//===--- RenamingAction.cpp - Clang refactoring library -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides an action to rename every symbol at a point.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
+#include "clang/Tooling/Tooling.h"
+#include <string>
+#include <vector>
+
+using namespace llvm;
+
+namespace clang {
+namespace tooling {
+
+class RenamingASTConsumer : public ASTConsumer {
+public:
+ RenamingASTConsumer(
+ const std::vector<std::string> &NewNames,
+ const std::vector<std::string> &PrevNames,
+ const std::vector<std::vector<std::string>> &USRList,
+ std::map<std::string, tooling::Replacements> &FileToReplaces,
+ bool PrintLocations)
+ : NewNames(NewNames), PrevNames(PrevNames), USRList(USRList),
+ FileToReplaces(FileToReplaces), PrintLocations(PrintLocations) {}
+
+ void HandleTranslationUnit(ASTContext &Context) override {
+ for (unsigned I = 0; I < NewNames.size(); ++I)
+ HandleOneRename(Context, NewNames[I], PrevNames[I], USRList[I]);
+ }
+
+ void HandleOneRename(ASTContext &Context, const std::string &NewName,
+ const std::string &PrevName,
+ const std::vector<std::string> &USRs) {
+ const SourceManager &SourceMgr = Context.getSourceManager();
+ std::vector<SourceLocation> RenamingCandidates;
+ std::vector<SourceLocation> NewCandidates;
+
+ NewCandidates = tooling::getLocationsOfUSRs(
+ USRs, PrevName, Context.getTranslationUnitDecl());
+ RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(),
+ NewCandidates.end());
+
+ unsigned PrevNameLen = PrevName.length();
+ for (const auto &Loc : RenamingCandidates) {
+ if (PrintLocations) {
+ FullSourceLoc FullLoc(Loc, SourceMgr);
+ errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(Loc)
+ << ":" << FullLoc.getSpellingLineNumber() << ":"
+ << FullLoc.getSpellingColumnNumber() << "\n";
+ }
+ // FIXME: better error handling.
+ tooling::Replacement Replace(SourceMgr, Loc, PrevNameLen, NewName);
+ 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, &PrevNames;
+ const std::vector<std::vector<std::string>> &USRList;
+ std::map<std::string, tooling::Replacements> &FileToReplaces;
+ 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 = tooling::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);
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/clang/lib/Tooling/Refactoring/Rename/USRFinder.cpp b/clang/lib/Tooling/Refactoring/Rename/USRFinder.cpp
new file mode 100644
index 00000000000..f36387dafdb
--- /dev/null
+++ b/clang/lib/Tooling/Refactoring/Rename/USRFinder.cpp
@@ -0,0 +1,213 @@
+//===--- USRFinder.cpp - Clang refactoring library ------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file Implements a recursive AST visitor that finds the USR of a symbol at a
+/// point.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Index/USRGeneration.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace tooling {
+
+// NamedDeclFindingASTVisitor recursively visits each AST node to find the
+// symbol underneath the cursor.
+// FIXME: move to separate .h/.cc file if this gets too large.
+namespace {
+class NamedDeclFindingASTVisitor
+ : public clang::RecursiveASTVisitor<NamedDeclFindingASTVisitor> {
+public:
+ // \brief Finds the NamedDecl at a point in the source.
+ // \param Point the location in the source to search for the NamedDecl.
+ explicit NamedDeclFindingASTVisitor(const SourceLocation Point,
+ const ASTContext &Context)
+ : Result(nullptr), Point(Point), Context(Context) {}
+
+ // \brief Finds the NamedDecl for a name in the source.
+ // \param Name the fully qualified name.
+ explicit NamedDeclFindingASTVisitor(const std::string &Name,
+ const ASTContext &Context)
+ : Result(nullptr), Name(Name), Context(Context) {}
+
+ // Declaration visitors:
+
+ // \brief Checks if the point falls within the NameDecl. This covers every
+ // declaration of a named entity that we may come across. Usually, just
+ // checking if the point lies within the length of the name of the declaration
+ // and the start location is sufficient.
+ bool VisitNamedDecl(const NamedDecl *Decl) {
+ return dyn_cast<CXXConversionDecl>(Decl)
+ ? true
+ : setResult(Decl, Decl->getLocation(),
+ Decl->getNameAsString().length());
+ }
+
+ // Expression visitors:
+
+ bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
+ const NamedDecl *Decl = Expr->getFoundDecl();
+ return setResult(Decl, Expr->getLocation(),
+ Decl->getNameAsString().length());
+ }
+
+ bool VisitMemberExpr(const MemberExpr *Expr) {
+ const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
+ return setResult(Decl, Expr->getMemberLoc(),
+ Decl->getNameAsString().length());
+ }
+
+ // Other visitors:
+
+ bool VisitTypeLoc(const TypeLoc Loc) {
+ const SourceLocation TypeBeginLoc = Loc.getBeginLoc();
+ const SourceLocation TypeEndLoc = Lexer::getLocForEndOfToken(
+ TypeBeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
+ if (const auto *TemplateTypeParm =
+ dyn_cast<TemplateTypeParmType>(Loc.getType()))
+ return setResult(TemplateTypeParm->getDecl(), TypeBeginLoc, TypeEndLoc);
+ if (const auto *TemplateSpecType =
+ dyn_cast<TemplateSpecializationType>(Loc.getType())) {
+ return setResult(TemplateSpecType->getTemplateName().getAsTemplateDecl(),
+ TypeBeginLoc, TypeEndLoc);
+ }
+ return setResult(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc,
+ TypeEndLoc);
+ }
+
+ bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
+ for (const auto *Initializer : ConstructorDecl->inits()) {
+ // Ignore implicit initializers.
+ if (!Initializer->isWritten())
+ continue;
+ if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
+ const SourceLocation InitBeginLoc = Initializer->getSourceLocation(),
+ InitEndLoc = Lexer::getLocForEndOfToken(
+ InitBeginLoc, 0, Context.getSourceManager(),
+ Context.getLangOpts());
+ if (!setResult(FieldDecl, InitBeginLoc, InitEndLoc))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Other:
+
+ const NamedDecl *getNamedDecl() { return Result; }
+
+ // \brief Determines if a namespace qualifier contains the point.
+ // \returns false on success and sets Result.
+ void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
+ while (NameLoc) {
+ const NamespaceDecl *Decl =
+ NameLoc.getNestedNameSpecifier()->getAsNamespace();
+ setResult(Decl, NameLoc.getLocalBeginLoc(), NameLoc.getLocalEndLoc());
+ NameLoc = NameLoc.getPrefix();
+ }
+ }
+
+private:
+ // \brief Sets Result to Decl if the Point is within Start and End.
+ // \returns false on success.
+ bool setResult(const NamedDecl *Decl, SourceLocation Start,
+ SourceLocation End) {
+ if (!Decl)
+ return true;
+ if (Name.empty()) {
+ // Offset is used to find the declaration.
+ if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
+ !End.isFileID() || !isPointWithin(Start, End))
+ return true;
+ } else {
+ // Fully qualified name is used to find the declaration.
+ if (Name != Decl->getQualifiedNameAsString() &&
+ Name != "::" + Decl->getQualifiedNameAsString())
+ return true;
+ }
+ Result = Decl;
+ return false;
+ }
+
+ // \brief Sets Result to Decl if Point is within Loc and Loc + Offset.
+ // \returns false on success.
+ bool setResult(const NamedDecl *Decl, SourceLocation Loc, unsigned Offset) {
+ // FIXME: Add test for Offset == 0. Add test for Offset - 1 (vs -2 etc).
+ return Offset == 0 ||
+ setResult(Decl, Loc, Loc.getLocWithOffset(Offset - 1));
+ }
+
+ // \brief Determines if the Point is within Start and End.
+ bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
+ // FIXME: Add tests for Point == End.
+ return Point == Start || Point == End ||
+ (Context.getSourceManager().isBeforeInTranslationUnit(Start,
+ Point) &&
+ Context.getSourceManager().isBeforeInTranslationUnit(Point, End));
+ }
+
+ const NamedDecl *Result;
+ const SourceLocation Point; // The location to find the NamedDecl.
+ const std::string Name;
+ const ASTContext &Context;
+};
+} // namespace
+
+const NamedDecl *getNamedDeclAt(const ASTContext &Context,
+ const SourceLocation Point) {
+ const SourceManager &SM = Context.getSourceManager();
+ NamedDeclFindingASTVisitor Visitor(Point, Context);
+
+ // Try to be clever about pruning down the number of top-level declarations we
+ // see. If both start and end is either before or after the point we're
+ // looking for the point cannot be inside of this decl. Don't even look at it.
+ for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
+ SourceLocation StartLoc = CurrDecl->getLocStart();
+ SourceLocation EndLoc = CurrDecl->getLocEnd();
+ if (StartLoc.isValid() && EndLoc.isValid() &&
+ SM.isBeforeInTranslationUnit(StartLoc, Point) !=
+ SM.isBeforeInTranslationUnit(EndLoc, Point))
+ Visitor.TraverseDecl(CurrDecl);
+ }
+
+ NestedNameSpecifierLocFinder Finder(const_cast<ASTContext &>(Context));
+ for (const auto &Location : Finder.getNestedNameSpecifierLocations())
+ Visitor.handleNestedNameSpecifierLoc(Location);
+
+ return Visitor.getNamedDecl();
+}
+
+const NamedDecl *getNamedDeclFor(const ASTContext &Context,
+ const std::string &Name) {
+ NamedDeclFindingASTVisitor Visitor(Name, Context);
+ Visitor.TraverseDecl(Context.getTranslationUnitDecl());
+
+ return Visitor.getNamedDecl();
+}
+
+std::string getUSRForDecl(const Decl *Decl) {
+ llvm::SmallVector<char, 128> Buff;
+
+ // FIXME: Add test for the nullptr case.
+ if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
+ return "";
+
+ return std::string(Buff.data(), Buff.size());
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/clang/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp b/clang/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp
new file mode 100644
index 00000000000..2769802ad2b
--- /dev/null
+++ b/clang/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp
@@ -0,0 +1,236 @@
+//===--- USRFindingAction.cpp - Clang refactoring library -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides an action to find USR for the symbol at <offset>, as well as
+/// all additional USRs.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
+#include "clang/Tooling/Tooling.h"
+
+#include <algorithm>
+#include <set>
+#include <string>
+#include <vector>
+
+using namespace llvm;
+
+namespace clang {
+namespace tooling {
+
+namespace {
+// \brief NamedDeclFindingConsumer should delegate finding USRs of given Decl to
+// AdditionalUSRFinder. AdditionalUSRFinder adds USRs of ctor and dtor if given
+// Decl refers to class and adds USRs of all overridden methods if Decl refers
+// to virtual method.
+class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {
+public:
+ AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context)
+ : FoundDecl(FoundDecl), Context(Context) {}
+
+ std::vector<std::string> Find() {
+ // Fill OverriddenMethods and PartialSpecs storages.
+ TraverseDecl(Context.getTranslationUnitDecl());
+ if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) {
+ addUSRsOfOverridenFunctions(MethodDecl);
+ for (const auto &OverriddenMethod : OverriddenMethods) {
+ if (checkIfOverriddenFunctionAscends(OverriddenMethod))
+ USRSet.insert(getUSRForDecl(OverriddenMethod));
+ }
+ } else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {
+ handleCXXRecordDecl(RecordDecl);
+ } else if (const auto *TemplateDecl =
+ dyn_cast<ClassTemplateDecl>(FoundDecl)) {
+ handleClassTemplateDecl(TemplateDecl);
+ } else {
+ USRSet.insert(getUSRForDecl(FoundDecl));
+ }
+ return std::vector<std::string>(USRSet.begin(), USRSet.end());
+ }
+
+ bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
+ if (MethodDecl->isVirtual())
+ OverriddenMethods.push_back(MethodDecl);
+ return true;
+ }
+
+ bool VisitClassTemplatePartialSpecializationDecl(
+ const ClassTemplatePartialSpecializationDecl *PartialSpec) {
+ PartialSpecs.push_back(PartialSpec);
+ return true;
+ }
+
+private:
+ void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
+ RecordDecl = RecordDecl->getDefinition();
+ if (const auto *ClassTemplateSpecDecl =
+ dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
+ handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
+ addUSRsOfCtorDtors(RecordDecl);
+ }
+
+ void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
+ for (const auto *Specialization : TemplateDecl->specializations())
+ addUSRsOfCtorDtors(Specialization);
+
+ for (const auto *PartialSpec : PartialSpecs) {
+ if (PartialSpec->getSpecializedTemplate() == TemplateDecl)
+ addUSRsOfCtorDtors(PartialSpec);
+ }
+ addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
+ }
+
+ 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));
+
+ USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
+ USRSet.insert(getUSRForDecl(RecordDecl));
+ }
+
+ void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
+ USRSet.insert(getUSRForDecl(MethodDecl));
+ // Recursively visit each OverridenMethod.
+ for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
+ addUSRsOfOverridenFunctions(OverriddenMethod);
+ }
+
+ bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
+ for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
+ if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
+ return true;
+ return checkIfOverriddenFunctionAscends(OverriddenMethod);
+ }
+ return false;
+ }
+
+ const Decl *FoundDecl;
+ ASTContext &Context;
+ std::set<std::string> USRSet;
+ std::vector<const CXXMethodDecl *> OverriddenMethods;
+ std::vector<const ClassTemplatePartialSpecializationDecl *> PartialSpecs;
+};
+} // namespace
+
+class NamedDeclFindingConsumer : public ASTConsumer {
+public:
+ NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
+ ArrayRef<std::string> QualifiedNames,
+ std::vector<std::string> &SpellingNames,
+ std::vector<std::vector<std::string>> &USRList,
+ bool Force, bool &ErrorOccurred)
+ : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
+ SpellingNames(SpellingNames), USRList(USRList), Force(Force),
+ ErrorOccurred(ErrorOccurred) {}
+
+private:
+ bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
+ unsigned SymbolOffset, const std::string &QualifiedName) {
+ DiagnosticsEngine &Engine = Context.getDiagnostics();
+ const FileID MainFileID = SourceMgr.getMainFileID();
+
+ if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
+ ErrorOccurred = true;
+ unsigned InvalidOffset = Engine.getCustomDiagID(
+ DiagnosticsEngine::Error,
+ "SourceLocation in file %0 at offset %1 is invalid");
+ Engine.Report(SourceLocation(), InvalidOffset)
+ << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
+ return false;
+ }
+
+ const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
+ .getLocWithOffset(SymbolOffset);
+ const NamedDecl *FoundDecl = QualifiedName.empty()
+ ? getNamedDeclAt(Context, Point)
+ : getNamedDeclFor(Context, QualifiedName);
+
+ if (FoundDecl == nullptr) {
+ if (QualifiedName.empty()) {
+ FullSourceLoc FullLoc(Point, SourceMgr);
+ unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
+ DiagnosticsEngine::Error,
+ "clang-rename could not find symbol (offset %0)");
+ Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
+ ErrorOccurred = true;
+ return false;
+ }
+
+ if (Force)
+ return true;
+
+ unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
+ DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
+ Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
+ ErrorOccurred = true;
+ return false;
+ }
+
+ // If FoundDecl is a constructor or destructor, we want to instead take
+ // the Decl of the corresponding class.
+ if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl))
+ FoundDecl = CtorDecl->getParent();
+ else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl))
+ FoundDecl = DtorDecl->getParent();
+
+ SpellingNames.push_back(FoundDecl->getNameAsString());
+ AdditionalUSRFinder Finder(FoundDecl, Context);
+ USRList.push_back(Finder.Find());
+ return true;
+ }
+
+ void HandleTranslationUnit(ASTContext &Context) override {
+ const SourceManager &SourceMgr = Context.getSourceManager();
+ for (unsigned Offset : SymbolOffsets) {
+ if (!FindSymbol(Context, SourceMgr, Offset, ""))
+ return;
+ }
+ for (const std::string &QualifiedName : QualifiedNames) {
+ if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
+ return;
+ }
+ }
+
+ ArrayRef<unsigned> SymbolOffsets;
+ ArrayRef<std::string> QualifiedNames;
+ std::vector<std::string> &SpellingNames;
+ std::vector<std::vector<std::string>> &USRList;
+ bool Force;
+ bool &ErrorOccurred;
+};
+
+std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
+ return llvm::make_unique<NamedDeclFindingConsumer>(
+ SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
+ ErrorOccurred);
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp b/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
new file mode 100644
index 00000000000..934507fe6ea
--- /dev/null
+++ b/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
@@ -0,0 +1,509 @@
+//===--- USRLocFinder.cpp - Clang refactoring library ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Methods for finding all instances of a USR. Our strategy is very
+/// simple; we just compare the USR at every relevant AST node with the one
+/// provided.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Core/Lookup.h"
+#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include <cstddef>
+#include <set>
+#include <string>
+#include <vector>
+
+using namespace llvm;
+
+namespace clang {
+namespace tooling {
+
+namespace {
+
+// \brief This visitor recursively searches for all instances of a USR in a
+// translation unit and stores them for later usage.
+class USRLocFindingASTVisitor
+ : public clang::RecursiveASTVisitor<USRLocFindingASTVisitor> {
+public:
+ explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs,
+ StringRef PrevName,
+ const ASTContext &Context)
+ : USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
+ }
+
+ // Declaration visitors:
+
+ bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
+ for (const auto *Initializer : ConstructorDecl->inits()) {
+ // Ignore implicit initializers.
+ if (!Initializer->isWritten())
+ continue;
+ if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
+ if (USRSet.find(getUSRForDecl(FieldDecl)) != USRSet.end())
+ LocationsFound.push_back(Initializer->getSourceLocation());
+ }
+ }
+ return true;
+ }
+
+ bool VisitNamedDecl(const NamedDecl *Decl) {
+ if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
+ checkAndAddLocation(Decl->getLocation());
+ return true;
+ }
+
+ // Expression visitors:
+
+ bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
+ const NamedDecl *Decl = Expr->getFoundDecl();
+
+ if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
+ const SourceManager &Manager = Decl->getASTContext().getSourceManager();
+ SourceLocation Location = Manager.getSpellingLoc(Expr->getLocation());
+ checkAndAddLocation(Location);
+ }
+
+ return true;
+ }
+
+ bool VisitMemberExpr(const MemberExpr *Expr) {
+ const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
+ if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
+ const SourceManager &Manager = Decl->getASTContext().getSourceManager();
+ SourceLocation Location = Manager.getSpellingLoc(Expr->getMemberLoc());
+ checkAndAddLocation(Location);
+ }
+ return true;
+ }
+
+ // Other visitors:
+
+ bool VisitTypeLoc(const TypeLoc Loc) {
+ if (USRSet.find(getUSRForDecl(Loc.getType()->getAsCXXRecordDecl())) !=
+ USRSet.end())
+ checkAndAddLocation(Loc.getBeginLoc());
+ if (const auto *TemplateTypeParm =
+ dyn_cast<TemplateTypeParmType>(Loc.getType())) {
+ if (USRSet.find(getUSRForDecl(TemplateTypeParm->getDecl())) !=
+ USRSet.end())
+ checkAndAddLocation(Loc.getBeginLoc());
+ }
+ return true;
+ }
+
+ // Non-visitors:
+
+ // \brief Returns a list of unique locations. Duplicate or overlapping
+ // locations are erroneous and should be reported!
+ const std::vector<clang::SourceLocation> &getLocationsFound() const {
+ return LocationsFound;
+ }
+
+ // Namespace traversal:
+ void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
+ while (NameLoc) {
+ const NamespaceDecl *Decl =
+ NameLoc.getNestedNameSpecifier()->getAsNamespace();
+ if (Decl && USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
+ checkAndAddLocation(NameLoc.getLocalBeginLoc());
+ NameLoc = NameLoc.getPrefix();
+ }
+ }
+
+private:
+ void checkAndAddLocation(SourceLocation Loc) {
+ const SourceLocation BeginLoc = Loc;
+ const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
+ StringRef TokenName =
+ Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
+ Context.getSourceManager(), Context.getLangOpts());
+ size_t Offset = TokenName.find(PrevName);
+
+ // The token of the source location we find actually has the old
+ // name.
+ if (Offset != StringRef::npos)
+ LocationsFound.push_back(BeginLoc.getLocWithOffset(Offset));
+ }
+
+ const std::set<std::string> USRSet;
+ const std::string PrevName;
+ std::vector<clang::SourceLocation> LocationsFound;
+ 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(const 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>
+getLocationsOfUSRs(const std::vector<std::string> &USRs, StringRef PrevName,
+ Decl *Decl) {
+ USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
+ Visitor.TraverseDecl(Decl);
+ NestedNameSpecifierLocFinder Finder(Decl->getASTContext());
+
+ for (const auto &Location : Finder.getNestedNameSpecifierLocations())
+ Visitor.handleNestedNameSpecifierLoc(Location);
+
+ 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;
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt
index 23d23bcddcc..fa926c584f8 100644
--- a/clang/test/CMakeLists.txt
+++ b/clang/test/CMakeLists.txt
@@ -47,6 +47,7 @@ list(APPEND CLANG_TEST_DEPS
clang-tblgen
clang-offload-bundler
clang-import-test
+ clang-rename
)
if(CLANG_ENABLE_STATIC_ANALYZER)
diff --git a/clang/test/clang-rename/ClassAsTemplateArgument.cpp b/clang/test/clang-rename/ClassAsTemplateArgument.cpp
new file mode 100644
index 00000000000..2e09a5b529e
--- /dev/null
+++ b/clang/test/clang-rename/ClassAsTemplateArgument.cpp
@@ -0,0 +1,21 @@
+class Foo /* Test 1 */ {}; // CHECK: class Bar /* Test 1 */ {};
+
+template <typename T>
+void func() {}
+
+template <typename T>
+class Baz {};
+
+int main() {
+ func<Foo>(); // CHECK: func<Bar>();
+ Baz<Foo> /* Test 2 */ obj; // CHECK: Baz<Bar> /* Test 2 */ obj;
+ return 0;
+}
+
+// Test 1.
+// RUN: clang-rename -offset=7 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=215 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'Foo.*' <file>
diff --git a/clang/test/clang-rename/ClassFindByName.cpp b/clang/test/clang-rename/ClassFindByName.cpp
new file mode 100644
index 00000000000..4430891ec4b
--- /dev/null
+++ b/clang/test/clang-rename/ClassFindByName.cpp
@@ -0,0 +1,10 @@
+class Foo { // CHECK: class Bar {
+};
+
+int main() {
+ Foo *Pointer = 0; // CHECK: Bar *Pointer = 0;
+ return 0;
+}
+
+// Test 1.
+// RUN: clang-rename -qualified-name=Foo -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
diff --git a/clang/test/clang-rename/ClassReplacements.cpp b/clang/test/clang-rename/ClassReplacements.cpp
new file mode 100644
index 00000000000..2b478bbf900
--- /dev/null
+++ b/clang/test/clang-rename/ClassReplacements.cpp
@@ -0,0 +1,11 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t/fixes
+// RUN: cat %s > %t.cpp
+// RUN: clang-rename -offset=254 -new-name=Bar -export-fixes=%t/fixes/clang-rename.yaml %t.cpp --
+// RUN: clang-apply-replacements %t
+// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s
+
+class Foo {}; // CHECK: class Bar {};
+
+// Use grep -FUbo 'Foo' <file> to get the correct offset of Cla when changing
+// this file.
diff --git a/clang/test/clang-rename/ClassSimpleRenaming.cpp b/clang/test/clang-rename/ClassSimpleRenaming.cpp
new file mode 100644
index 00000000000..086f55736cb
--- /dev/null
+++ b/clang/test/clang-rename/ClassSimpleRenaming.cpp
@@ -0,0 +1,14 @@
+class Foo /* Test 1 */ { // CHECK: class Bar /* Test 1 */ {
+public:
+ void foo(int x);
+};
+
+void Foo::foo(int x) /* Test 2 */ {} // CHECK: void Bar::foo(int x) /* Test 2 */ {}
+
+// Test 1.
+// RUN: clang-rename -offset=6 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=109 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'Foo.*' <file>
diff --git a/clang/test/clang-rename/ClassTestMulti.cpp b/clang/test/clang-rename/ClassTestMulti.cpp
new file mode 100644
index 00000000000..81e65c76065
--- /dev/null
+++ b/clang/test/clang-rename/ClassTestMulti.cpp
@@ -0,0 +1,11 @@
+class Foo1 /* Offset 1 */ { // CHECK: class Bar1 /* Offset 1 */ {
+};
+
+class Foo2 /* Offset 2 */ { // CHECK: class Bar2 /* Offset 2 */ {
+};
+
+// Test 1.
+// RUN: clang-rename -offset=6 -new-name=Bar1 -offset=76 -new-name=Bar2 %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'Foo.*' <file>
diff --git a/clang/test/clang-rename/ClassTestMultiByName.cpp b/clang/test/clang-rename/ClassTestMultiByName.cpp
new file mode 100644
index 00000000000..61b69a1bdf4
--- /dev/null
+++ b/clang/test/clang-rename/ClassTestMultiByName.cpp
@@ -0,0 +1,8 @@
+class Foo1 { // CHECK: class Bar1
+};
+
+class Foo2 { // CHECK: class Bar2
+};
+
+// Test 1.
+// RUN: clang-rename -qualified-name=Foo1 -new-name=Bar1 -qualified-name=Foo2 -new-name=Bar2 %s -- | sed 's,//.*,,' | FileCheck %s
diff --git a/clang/test/clang-rename/ComplexFunctionOverride.cpp b/clang/test/clang-rename/ComplexFunctionOverride.cpp
new file mode 100644
index 00000000000..ccf3a20e540
--- /dev/null
+++ b/clang/test/clang-rename/ComplexFunctionOverride.cpp
@@ -0,0 +1,47 @@
+struct A {
+ virtual void foo() {} /* Test 1 */ // CHECK: virtual void bar() {}
+};
+
+struct B : A {
+ void foo() override {} /* Test 2 */ // CHECK: void bar() override {}
+};
+
+struct C : B {
+ void foo() override {} /* Test 3 */ // CHECK: void bar() override {}
+};
+
+struct D : B {
+ void foo() override {} /* Test 4 */ // CHECK: void bar() override {}
+};
+
+struct E : D {
+ void foo() override {} /* Test 5 */ // CHECK: void bar() override {}
+};
+
+int main() {
+ A a;
+ a.foo(); // CHECK: a.bar();
+ B b;
+ b.foo(); // CHECK: b.bar();
+ C c;
+ c.foo(); // CHECK: c.bar();
+ D d;
+ d.foo(); // CHECK: d.bar();
+ E e;
+ e.foo(); // CHECK: e.bar();
+ return 0;
+}
+
+// Test 1.
+// RUN: clang-rename -offset=26 -new-name=bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=109 -new-name=bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 3.
+// RUN: clang-rename -offset=201 -new-name=bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 4.
+// RUN: clang-rename -offset=293 -new-name=bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 5.
+// RUN: clang-rename -offset=385 -new-name=bar %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'foo.*' <file>
diff --git a/clang/test/clang-rename/ComplicatedClassType.cpp b/clang/test/clang-rename/ComplicatedClassType.cpp
new file mode 100644
index 00000000000..88019530312
--- /dev/null
+++ b/clang/test/clang-rename/ComplicatedClassType.cpp
@@ -0,0 +1,63 @@
+// Forward declaration.
+class Foo; /* Test 1 */ // CHECK: class Bar; /* Test 1 */
+
+class Baz {
+ virtual int getValue() const = 0;
+};
+
+class Foo : public Baz { /* Test 2 */// CHECK: class Bar : public Baz {
+public:
+ Foo(int value = 0) : x(value) {} // CHECK: Bar(int value = 0) : x(value) {}
+
+ Foo &operator++(int) { // CHECK: Bar &operator++(int) {
+ x++;
+ return *this;
+ }
+
+ bool operator<(Foo const &rhs) { // CHECK: bool operator<(Bar const &rhs) {
+ return this->x < rhs.x;
+ }
+
+ int getValue() const {
+ return 0;
+ }
+
+private:
+ int x;
+};
+
+int main() {
+ Foo *Pointer = 0; // CHECK: Bar *Pointer = 0;
+ Foo Variable = Foo(10); // CHECK: Bar Variable = Bar(10);
+ for (Foo it; it < Variable; it++) { // CHECK: for (Bar it; it < Variable; it++) {
+ }
+ const Foo *C = new Foo(); // CHECK: const Bar *C = new Bar();
+ const_cast<Foo *>(C)->getValue(); // CHECK: const_cast<Bar *>(C)->getValue();
+ Foo foo; // CHECK: Bar foo;
+ const Baz &BazReference = foo;
+ const Baz *BazPointer = &foo;
+ dynamic_cast<const Foo &>(BazReference).getValue(); /* Test 3 */ // CHECK: dynamic_cast<const Bar &>(BazReference).getValue();
+ dynamic_cast<const Foo *>(BazPointer)->getValue(); /* Test 4 */ // CHECK: dynamic_cast<const Bar *>(BazPointer)->getValue();
+ reinterpret_cast<const Foo *>(BazPointer)->getValue(); /* Test 5 */ // CHECK: reinterpret_cast<const Bar *>(BazPointer)->getValue();
+ static_cast<const Foo &>(BazReference).getValue(); /* Test 6 */ // CHECK: static_cast<const Bar &>(BazReference).getValue();
+ static_cast<const Foo *>(BazPointer)->getValue(); /* Test 7 */ // CHECK: static_cast<const Bar *>(BazPointer)->getValue();
+ return 0;
+}
+
+// Test 1.
+// RUN: clang-rename -offset=30 -new-name=Bar %s -- -frtti | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=155 -new-name=Bar %s -- -frtti | sed 's,//.*,,' | FileCheck %s
+// Test 3.
+// RUN: clang-rename -offset=1133 -new-name=Bar %s -- -frtti | sed 's,//.*,,' | FileCheck %s
+// Test 4.
+// RUN: clang-rename -offset=1266 -new-name=Bar %s -- -frtti | sed 's,//.*,,' | FileCheck %s
+// Test 5.
+// RUN: clang-rename -offset=1402 -new-name=Bar %s -- -frtti | sed 's,//.*,,' | FileCheck %s
+// Test 6.
+// RUN: clang-rename -offset=1533 -new-name=Bar %s -- -frtti | sed 's,//.*,,' | FileCheck %s
+// Test 7.
+// RUN: clang-rename -offset=1665 -new-name=Bar %s -- -frtti | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'Foo.*' <file>
diff --git a/clang/test/clang-rename/Ctor.cpp b/clang/test/clang-rename/Ctor.cpp
new file mode 100644
index 00000000000..9908a4123dd
--- /dev/null
+++ b/clang/test/clang-rename/Ctor.cpp
@@ -0,0 +1,14 @@
+class Foo { // CHECK: class Bar {
+public:
+ Foo(); /* Test 1 */ // CHECK: Bar();
+};
+
+Foo::Foo() /* Test 2 */ {} // CHECK: Bar::Bar() /* Test 2 */ {}
+
+// Test 1.
+// RUN: clang-rename -offset=62 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=116 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'Foo.*' <file>
diff --git a/clang/test/clang-rename/CtorInitializer.cpp b/clang/test/clang-rename/CtorInitializer.cpp
new file mode 100644
index 00000000000..fed4f5b06c2
--- /dev/null
+++ b/clang/test/clang-rename/CtorInitializer.cpp
@@ -0,0 +1,17 @@
+class Baz {};
+
+class Qux {
+ Baz Foo; /* Test 1 */ // CHECK: Baz Bar;
+public:
+ Qux();
+};
+
+Qux::Qux() : Foo() /* Test 2 */ {} // CHECK: Qux::Qux() : Bar() /* Test 2 */ {}
+
+// Test 1.
+// RUN: clang-rename -offset=33 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=118 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'Foo.*' <file>
diff --git a/clang/test/clang-rename/DeclRefExpr.cpp b/clang/test/clang-rename/DeclRefExpr.cpp
new file mode 100644
index 00000000000..6462862d82a
--- /dev/null
+++ b/clang/test/clang-rename/DeclRefExpr.cpp
@@ -0,0 +1,24 @@
+class C {
+public:
+ static int Foo; /* Test 1 */ // CHECK: static int Bar;
+};
+
+int foo(int x) { return 0; }
+#define MACRO(a) foo(a)
+
+int main() {
+ C::Foo = 1; /* Test 2 */ // CHECK: C::Bar = 1;
+ MACRO(C::Foo); // CHECK: MACRO(C::Bar);
+ int y = C::Foo; /* Test 3 */ // CHECK: int y = C::Bar;
+ return 0;
+}
+
+// Test 1.
+// RUN: clang-rename -offset=31 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=152 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 3.
+// RUN: clang-rename -offset=271 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'Foo.*' <file>
diff --git a/clang/test/clang-rename/Field.cpp b/clang/test/clang-rename/Field.cpp
new file mode 100644
index 00000000000..c0e9a019e47
--- /dev/null
+++ b/clang/test/clang-rename/Field.cpp
@@ -0,0 +1,15 @@
+class Baz {
+ int Foo; /* Test 1 */ // CHECK: int Bar;
+public:
+ Baz();
+};
+
+Baz::Baz() : Foo(0) /* Test 2 */ {} // CHECK: Baz::Baz() : Bar(0)
+
+// Test 1.
+// RUN: clang-rename -offset=18 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=89 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'Foo.*' <file>
diff --git a/clang/test/clang-rename/FunctionMacro.cpp b/clang/test/clang-rename/FunctionMacro.cpp
new file mode 100644
index 00000000000..6e87026ec70
--- /dev/null
+++ b/clang/test/clang-rename/FunctionMacro.cpp
@@ -0,0 +1,20 @@
+#define moo foo // CHECK: #define moo macro_function
+
+int foo() /* Test 1 */ { // CHECK: int macro_function() /* Test 1 */ {
+ return 42;
+}
+
+void boo(int value) {}
+
+void qoo() {
+ foo(); // CHECK: macro_function();
+ boo(foo()); // CHECK: boo(macro_function());
+ moo();
+ boo(moo());
+}
+
+// Test 1.
+// RUN: clang-rename -offset=68 -new-name=macro_function %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'foo.*' <file>
diff --git a/clang/test/clang-rename/FunctionOverride.cpp b/clang/test/clang-rename/FunctionOverride.cpp
new file mode 100644
index 00000000000..adfeb739e66
--- /dev/null
+++ b/clang/test/clang-rename/FunctionOverride.cpp
@@ -0,0 +1,13 @@
+class A { virtual void foo(); /* Test 1 */ }; // CHECK: class A { virtual void bar();
+class B : public A { void foo(); /* Test 2 */ }; // CHECK: class B : public A { void bar();
+class C : public B { void foo(); /* Test 3 */ }; // CHECK: class C : public B { void bar();
+
+// Test 1.
+// RUN: clang-rename -offset=23 -new-name=bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=116 -new-name=bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 3.
+// RUN: clang-rename -offset=209 -new-name=bar %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'foo.*' <file>
diff --git a/clang/test/clang-rename/FunctionWithClassFindByName.cpp b/clang/test/clang-rename/FunctionWithClassFindByName.cpp
new file mode 100644
index 00000000000..2cae09a1c24
--- /dev/null
+++ b/clang/test/clang-rename/FunctionWithClassFindByName.cpp
@@ -0,0 +1,12 @@
+void foo() {
+}
+
+class Foo { // CHECK: class Bar
+};
+
+int main() {
+ Foo *Pointer = 0; // CHECK: Bar *Pointer = 0;
+ return 0;
+}
+
+// RUN: clang-rename -qualified-name=Foo -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
diff --git a/clang/test/clang-rename/IncludeHeaderWithSymbol.cpp b/clang/test/clang-rename/IncludeHeaderWithSymbol.cpp
new file mode 100644
index 00000000000..cb2baee57b8
--- /dev/null
+++ b/clang/test/clang-rename/IncludeHeaderWithSymbol.cpp
@@ -0,0 +1,10 @@
+#include "Inputs/HeaderWithSymbol.h"
+
+int main() {
+ return 0; // CHECK: {{^ return 0;}}
+}
+
+// Test 1.
+// The file IncludeHeaderWithSymbol.cpp doesn't contain the symbol Foo
+// and is expected to be written to stdout without modifications
+// RUN: clang-rename -qualified-name=Foo -new-name=Bar %s -- | FileCheck %s
diff --git a/clang/test/clang-rename/Inputs/HeaderWithSymbol.h b/clang/test/clang-rename/Inputs/HeaderWithSymbol.h
new file mode 100644
index 00000000000..1fe02e89786
--- /dev/null
+++ b/clang/test/clang-rename/Inputs/HeaderWithSymbol.h
@@ -0,0 +1 @@
+struct Foo {};
diff --git a/clang/test/clang-rename/Inputs/OffsetToNewName.yaml b/clang/test/clang-rename/Inputs/OffsetToNewName.yaml
new file mode 100644
index 00000000000..d8e972880f3
--- /dev/null
+++ b/clang/test/clang-rename/Inputs/OffsetToNewName.yaml
@@ -0,0 +1,6 @@
+---
+- Offset: 6
+ NewName: Bar1
+- Offset: 44
+ NewName: Bar2
+...
diff --git a/clang/test/clang-rename/Inputs/QualifiedNameToNewName.yaml b/clang/test/clang-rename/Inputs/QualifiedNameToNewName.yaml
new file mode 100644
index 00000000000..6e3783671df
--- /dev/null
+++ b/clang/test/clang-rename/Inputs/QualifiedNameToNewName.yaml
@@ -0,0 +1,6 @@
+---
+- QualifiedName: Foo1
+ NewName: Bar1
+- QualifiedName: Foo2
+ NewName: Bar2
+...
diff --git a/clang/test/clang-rename/InvalidNewName.cpp b/clang/test/clang-rename/InvalidNewName.cpp
new file mode 100644
index 00000000000..e6b38e59420
--- /dev/null
+++ b/clang/test/clang-rename/InvalidNewName.cpp
@@ -0,0 +1,2 @@
+// RUN: not clang-rename -new-name=class -offset=133 %s 2>&1 | FileCheck %s
+// CHECK: ERROR: new name is not a valid identifier in C++17.
diff --git a/clang/test/clang-rename/InvalidOffset.cpp b/clang/test/clang-rename/InvalidOffset.cpp
new file mode 100644
index 00000000000..2ae04d01e4a
--- /dev/null
+++ b/clang/test/clang-rename/InvalidOffset.cpp
@@ -0,0 +1,9 @@
+#include "Inputs/HeaderWithSymbol.h"
+#define FOO int bar;
+FOO
+
+int foo;
+
+// RUN: not clang-rename -new-name=qux -offset=259 %s -- 2>&1 | FileCheck %s
+// CHECK-NOT: CHECK
+// CHECK: error: SourceLocation in file {{.*}}InvalidOffset.cpp at offset 259 is invalid
diff --git a/clang/test/clang-rename/InvalidQualifiedName.cpp b/clang/test/clang-rename/InvalidQualifiedName.cpp
new file mode 100644
index 00000000000..5280e3939cc
--- /dev/null
+++ b/clang/test/clang-rename/InvalidQualifiedName.cpp
@@ -0,0 +1,4 @@
+struct S {
+};
+
+// RUN: clang-rename -force -qualified-name S2 -new-name=T %s --
diff --git a/clang/test/clang-rename/MemberExprMacro.cpp b/clang/test/clang-rename/MemberExprMacro.cpp
new file mode 100644
index 00000000000..56cd8d95f6e
--- /dev/null
+++ b/clang/test/clang-rename/MemberExprMacro.cpp
@@ -0,0 +1,22 @@
+class Baz {
+public:
+ int Foo; /* Test 1 */ // CHECK: int Bar;
+};
+
+int qux(int x) { return 0; }
+#define MACRO(a) qux(a)
+
+int main() {
+ Baz baz;
+ baz.Foo = 1; /* Test 2 */ // CHECK: baz.Bar = 1;
+ MACRO(baz.Foo); // CHECK: MACRO(baz.Bar);
+ int y = baz.Foo; // CHECK: int y = baz.Bar;
+}
+
+// Test 1.
+// RUN: clang-rename -offset=26 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=155 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'Foo.*' <file>
diff --git a/clang/test/clang-rename/Namespace.cpp b/clang/test/clang-rename/Namespace.cpp
new file mode 100644
index 00000000000..ec9630fdedb
--- /dev/null
+++ b/clang/test/clang-rename/Namespace.cpp
@@ -0,0 +1,13 @@
+namespace gcc /* Test 1 */ { // CHECK: namespace clang /* Test 1 */ {
+ int x;
+}
+
+void boo() {
+ gcc::x = 42; // CHECK: clang::x = 42;
+}
+
+// Test 1.
+// RUN: clang-rename -offset=10 -new-name=clang %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'Foo.*' <file>
diff --git a/clang/test/clang-rename/NoNewName.cpp b/clang/test/clang-rename/NoNewName.cpp
new file mode 100644
index 00000000000..4f882d83b0c
--- /dev/null
+++ b/clang/test/clang-rename/NoNewName.cpp
@@ -0,0 +1,4 @@
+// Check for an error while -new-name argument has not been passed to
+// clang-rename.
+// RUN: not clang-rename -offset=133 %s 2>&1 | FileCheck %s
+// CHECK: clang-rename: -new-name must be specified.
diff --git a/clang/test/clang-rename/TemplateClassInstantiation.cpp b/clang/test/clang-rename/TemplateClassInstantiation.cpp
new file mode 100644
index 00000000000..493d0951df5
--- /dev/null
+++ b/clang/test/clang-rename/TemplateClassInstantiation.cpp
@@ -0,0 +1,42 @@
+template <typename T>
+class Foo { /* Test 1 */ // CHECK: class Bar { /* Test 1 */
+public:
+ T foo(T arg, T& ref, T* ptr) {
+ T value;
+ int number = 42;
+ value = (T)number;
+ value = static_cast<T>(number);
+ return value;
+ }
+ static void foo(T value) {}
+ T member;
+};
+
+template <typename T>
+void func() {
+ Foo<T> obj; /* Test 2 */ // CHECK: Bar<T> obj;
+ obj.member = T();
+ Foo<T>::foo(); // CHECK: Bar<T>::foo();
+}
+
+int main() {
+ Foo<int> i; /* Test 3 */ // CHECK: Bar<int> i;
+ i.member = 0;
+ Foo<int>::foo(0); // CHECK: Bar<int>::foo(0);
+
+ Foo<bool> b; // CHECK: Bar<bool> b;
+ b.member = false;
+ Foo<bool>::foo(false); // CHECK: Bar<bool>::foo(false);
+
+ return 0;
+}
+
+// Test 1.
+// RUN: clang-rename -offset=29 -new-name=Bar %s -- -fno-delayed-template-parsing | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=324 -new-name=Bar %s -- -fno-delayed-template-parsing | sed 's,//.*,,' | FileCheck %s
+// Test 3.
+// RUN: clang-rename -offset=463 -new-name=Bar %s -- -fno-delayed-template-parsing | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'Foo.*' <file>
diff --git a/clang/test/clang-rename/TemplateTypename.cpp b/clang/test/clang-rename/TemplateTypename.cpp
new file mode 100644
index 00000000000..559ec1f9ade
--- /dev/null
+++ b/clang/test/clang-rename/TemplateTypename.cpp
@@ -0,0 +1,24 @@
+template <typename T /* Test 1 */> // CHECK: template <typename U /* Test 1 */>
+class Foo {
+T foo(T arg, T& ref, T* /* Test 2 */ ptr) { // CHECK: U foo(U arg, U& ref, U* /* Test 2 */ ptr) {
+ T value; // CHECK: U value;
+ int number = 42;
+ value = (T)number; // CHECK: value = (U)number;
+ value = static_cast<T /* Test 3 */>(number); // CHECK: value = static_cast<U /* Test 3 */>(number);
+ return value;
+}
+
+static void foo(T value) {} // CHECK: static void foo(U value) {}
+
+T member; // CHECK: U member;
+};
+
+// Test 1.
+// RUN: clang-rename -offset=19 -new-name=U %s -- -fno-delayed-template-parsing | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=126 -new-name=U %s -- -fno-delayed-template-parsing | sed 's,//.*,,' | FileCheck %s
+// Test 3.
+// RUN: clang-rename -offset=392 -new-name=U %s -- -fno-delayed-template-parsing | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'T.*' <file>
diff --git a/clang/test/clang-rename/TemplatedClassFunction.cpp b/clang/test/clang-rename/TemplatedClassFunction.cpp
new file mode 100644
index 00000000000..1f5b0b52ba7
--- /dev/null
+++ b/clang/test/clang-rename/TemplatedClassFunction.cpp
@@ -0,0 +1,22 @@
+template <typename T>
+class A {
+public:
+ void foo() /* Test 1 */ {} // CHECK: void bar() /* Test 1 */ {}
+};
+
+int main(int argc, char **argv) {
+ A<int> a;
+ a.foo(); /* Test 2 */ // CHECK: a.bar() /* Test 2 */
+ return 0;
+}
+
+// Test 1.
+// RUN: clang-refactor rename -offset=48 -new-name=bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-refactor rename -offset=162 -new-name=bar %s -- | sed 's,//.*,,' | FileCheck %s
+//
+// Currently unsupported test.
+// XFAIL: *
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'foo.*' <file>
diff --git a/clang/test/clang-rename/UserDefinedConversion.cpp b/clang/test/clang-rename/UserDefinedConversion.cpp
new file mode 100644
index 00000000000..60f251ab448
--- /dev/null
+++ b/clang/test/clang-rename/UserDefinedConversion.cpp
@@ -0,0 +1,26 @@
+class Foo { /* Test 1 */ // CHECK: class Bar {
+public:
+ Foo() {} // CHECK: Bar() {}
+};
+
+class Baz {
+public:
+ operator Foo() /* Test 2 */ const { // CHECK: operator Bar() /* Test 2 */ const {
+ Foo foo; // CHECK: Bar foo;
+ return foo;
+ }
+};
+
+int main() {
+ Baz boo;
+ Foo foo = static_cast<Foo>(boo); // CHECK: Bar foo = static_cast<Bar>(boo);
+ return 0;
+}
+
+// Test 1.
+// RUN: clang-rename -offset=7 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=164 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'Foo.*' <file>
diff --git a/clang/test/clang-rename/Variable.cpp b/clang/test/clang-rename/Variable.cpp
new file mode 100644
index 00000000000..d7e670fb43e
--- /dev/null
+++ b/clang/test/clang-rename/Variable.cpp
@@ -0,0 +1,33 @@
+#define NAMESPACE namespace A
+NAMESPACE {
+int Foo; /* Test 1 */ // CHECK: int Bar;
+}
+int Foo; // CHECK: int Foo;
+int Qux = Foo; // CHECK: int Qux = Foo;
+int Baz = A::Foo; /* Test 2 */ // CHECK: Baz = A::Bar;
+void fun() {
+ struct {
+ int Foo; // CHECK: int Foo;
+ } b = {100};
+ int Foo = 100; // CHECK: int Foo = 100;
+ Baz = Foo; // CHECK: Baz = Foo;
+ {
+ extern int Foo; // CHECK: extern int Foo;
+ Baz = Foo; // CHECK: Baz = Foo;
+ Foo = A::Foo /* Test 3 */ + Baz; // CHECK: Foo = A::Bar /* Test 3 */ + Baz;
+ A::Foo /* Test 4 */ = b.Foo; // CHECK: A::Bar /* Test 4 */ = b.Foo;
+ }
+ Foo = b.Foo; // Foo = b.Foo;
+}
+
+// Test 1.
+// RUN: clang-rename -offset=46 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=234 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 3.
+// RUN: clang-rename -offset=641 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 4.
+// RUN: clang-rename -offset=716 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'Foo.*' <file>
diff --git a/clang/test/clang-rename/VariableMacro.cpp b/clang/test/clang-rename/VariableMacro.cpp
new file mode 100644
index 00000000000..622e825d3e4
--- /dev/null
+++ b/clang/test/clang-rename/VariableMacro.cpp
@@ -0,0 +1,21 @@
+#define Baz Foo // CHECK: #define Baz Bar
+
+void foo(int value) {}
+
+void macro() {
+ int Foo; /* Test 1 */ // CHECK: int Bar;
+ Foo = 42; /* Test 2 */ // CHECK: Bar = 42;
+ Baz -= 0;
+ foo(Foo); /* Test 3 */ // CHECK: foo(Bar);
+ foo(Baz);
+}
+
+// Test 1.
+// RUN: clang-rename -offset=88 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -offset=129 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 3.
+// RUN: clang-rename -offset=191 -new-name=Bar %s -- | sed 's,//.*,,' | FileCheck %s
+
+// To find offsets after modifying the file, use:
+// grep -Ubo 'Foo.*' <file>
diff --git a/clang/test/clang-rename/YAMLInput.cpp b/clang/test/clang-rename/YAMLInput.cpp
new file mode 100644
index 00000000000..55dbc6d66a5
--- /dev/null
+++ b/clang/test/clang-rename/YAMLInput.cpp
@@ -0,0 +1,10 @@
+class Foo1 { // CHECK: class Bar1
+};
+
+class Foo2 { // CHECK: class Bar2
+};
+
+// Test 1.
+// RUN: clang-rename -input %S/Inputs/OffsetToNewName.yaml %s -- | sed 's,//.*,,' | FileCheck %s
+// Test 2.
+// RUN: clang-rename -input %S/Inputs/QualifiedNameToNewName.yaml %s -- | sed 's,//.*,,' | FileCheck %s
diff --git a/clang/tools/CMakeLists.txt b/clang/tools/CMakeLists.txt
index b0c97f0f1e4..4976332b7db 100644
--- a/clang/tools/CMakeLists.txt
+++ b/clang/tools/CMakeLists.txt
@@ -10,6 +10,8 @@ add_clang_subdirectory(clang-offload-bundler)
add_clang_subdirectory(c-index-test)
+add_clang_subdirectory(clang-rename)
+
if(CLANG_ENABLE_ARCMT)
add_clang_subdirectory(arcmt-test)
add_clang_subdirectory(c-arcmt-test)
diff --git a/clang/tools/clang-rename/CMakeLists.txt b/clang/tools/clang-rename/CMakeLists.txt
new file mode 100644
index 00000000000..f6a4f49f7aa
--- /dev/null
+++ b/clang/tools/clang-rename/CMakeLists.txt
@@ -0,0 +1,19 @@
+add_clang_executable(clang-rename ClangRename.cpp)
+
+target_link_libraries(clang-rename
+ clangBasic
+ clangFrontend
+ clangRewrite
+ clangTooling
+ clangToolingCore
+ clangToolingRefactor
+ )
+
+install(TARGETS clang-rename RUNTIME DESTINATION bin)
+
+install(PROGRAMS clang-rename.py
+ DESTINATION share/clang
+ COMPONENT clang-rename)
+install(PROGRAMS clang-rename.el
+ DESTINATION share/clang
+ COMPONENT clang-rename)
diff --git a/clang/tools/clang-rename/ClangRename.cpp b/clang/tools/clang-rename/ClangRename.cpp
new file mode 100644
index 00000000000..0fdf9a39809
--- /dev/null
+++ b/clang/tools/clang-rename/ClangRename.cpp
@@ -0,0 +1,240 @@
+//===--- tools/extra/clang-rename/ClangRename.cpp - Clang rename tool -----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file implements a clang-rename tool that automatically finds and
+/// renames symbols in C++ code.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
+#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
+#include "clang/Tooling/ReplacementsYaml.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstdlib>
+#include <string>
+#include <system_error>
+
+using namespace llvm;
+using namespace clang;
+
+/// \brief An oldname -> newname rename.
+struct RenameAllInfo {
+ unsigned Offset = 0;
+ std::string QualifiedName;
+ std::string NewName;
+};
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(RenameAllInfo)
+
+namespace llvm {
+namespace yaml {
+
+/// \brief Specialized MappingTraits to describe how a RenameAllInfo is
+/// (de)serialized.
+template <> struct MappingTraits<RenameAllInfo> {
+ static void mapping(IO &IO, RenameAllInfo &Info) {
+ IO.mapOptional("Offset", Info.Offset);
+ IO.mapOptional("QualifiedName", Info.QualifiedName);
+ IO.mapRequired("NewName", Info.NewName);
+ }
+};
+
+} // end namespace yaml
+} // end namespace llvm
+
+static cl::OptionCategory ClangRenameOptions("clang-rename common options");
+
+static cl::list<unsigned> SymbolOffsets(
+ "offset",
+ cl::desc("Locates the symbol by offset as opposed to <line>:<column>."),
+ cl::ZeroOrMore, cl::cat(ClangRenameOptions));
+static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited <file>s."),
+ cl::cat(ClangRenameOptions));
+static cl::list<std::string>
+ QualifiedNames("qualified-name",
+ cl::desc("The fully qualified name of the symbol."),
+ cl::ZeroOrMore, cl::cat(ClangRenameOptions));
+
+static cl::list<std::string>
+ NewNames("new-name", cl::desc("The new name to change the symbol to."),
+ cl::ZeroOrMore, cl::cat(ClangRenameOptions));
+static cl::opt<bool> PrintName(
+ "pn",
+ cl::desc("Print the found symbol's name prior to renaming to stderr."),
+ cl::cat(ClangRenameOptions));
+static cl::opt<bool> PrintLocations(
+ "pl", cl::desc("Print the locations affected by renaming to stderr."),
+ cl::cat(ClangRenameOptions));
+static cl::opt<std::string>
+ ExportFixes("export-fixes",
+ cl::desc("YAML file to store suggested fixes in."),
+ cl::value_desc("filename"), cl::cat(ClangRenameOptions));
+static cl::opt<std::string>
+ Input("input", cl::desc("YAML file to load oldname-newname pairs from."),
+ cl::Optional, cl::cat(ClangRenameOptions));
+static cl::opt<bool> Force("force",
+ cl::desc("Ignore nonexistent qualified names."),
+ cl::cat(ClangRenameOptions));
+
+int main(int argc, const char **argv) {
+ tooling::CommonOptionsParser OP(argc, argv, ClangRenameOptions);
+
+ if (!Input.empty()) {
+ // Populate QualifiedNames and NewNames from a YAML file.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
+ llvm::MemoryBuffer::getFile(Input);
+ if (!Buffer) {
+ errs() << "clang-rename: failed to read " << Input << ": "
+ << Buffer.getError().message() << "\n";
+ return 1;
+ }
+
+ std::vector<RenameAllInfo> Infos;
+ llvm::yaml::Input YAML(Buffer.get()->getBuffer());
+ YAML >> Infos;
+ for (const auto &Info : Infos) {
+ if (!Info.QualifiedName.empty())
+ QualifiedNames.push_back(Info.QualifiedName);
+ else
+ SymbolOffsets.push_back(Info.Offset);
+ NewNames.push_back(Info.NewName);
+ }
+ }
+
+ // Check the arguments for correctness.
+ if (NewNames.empty()) {
+ errs() << "clang-rename: -new-name must be specified.\n\n";
+ exit(1);
+ }
+
+ if (SymbolOffsets.empty() == QualifiedNames.empty()) {
+ errs() << "clang-rename: -offset and -qualified-name can't be present at "
+ "the same time.\n";
+ exit(1);
+ }
+
+ // Check if NewNames is a valid identifier in C++17.
+ LangOptions Options;
+ Options.CPlusPlus = true;
+ Options.CPlusPlus1z = true;
+ IdentifierTable Table(Options);
+ for (const auto &NewName : NewNames) {
+ auto NewNameTokKind = Table.get(NewName).getTokenID();
+ if (!tok::isAnyIdentifier(NewNameTokKind)) {
+ errs() << "ERROR: new name is not a valid identifier in C++17.\n\n";
+ exit(1);
+ }
+ }
+
+ if (SymbolOffsets.size() + QualifiedNames.size() != NewNames.size()) {
+ errs() << "clang-rename: number of symbol offsets(" << SymbolOffsets.size()
+ << ") + number of qualified names (" << QualifiedNames.size()
+ << ") must be equal to number of new names(" << NewNames.size()
+ << ").\n\n";
+ cl::PrintHelpMessage();
+ exit(1);
+ }
+
+ auto Files = OP.getSourcePathList();
+ tooling::RefactoringTool Tool(OP.getCompilations(), Files);
+ tooling::USRFindingAction FindingAction(SymbolOffsets, QualifiedNames, Force);
+ Tool.run(tooling::newFrontendActionFactory(&FindingAction).get());
+ const std::vector<std::vector<std::string>> &USRList =
+ FindingAction.getUSRList();
+ const std::vector<std::string> &PrevNames = FindingAction.getUSRSpellings();
+ if (PrintName) {
+ for (const auto &PrevName : PrevNames) {
+ outs() << "clang-rename found name: " << PrevName << '\n';
+ }
+ }
+
+ if (FindingAction.errorOccurred()) {
+ // Diagnostics are already issued at this point.
+ exit(1);
+ }
+
+ if (Force && PrevNames.size() < NewNames.size()) {
+ // No matching PrevName for all NewNames. Without Force this is an error
+ // above already.
+ exit(0);
+ }
+
+ // Perform the renaming.
+ tooling::RenamingAction RenameAction(NewNames, PrevNames, USRList,
+ Tool.getReplacements(), PrintLocations);
+ std::unique_ptr<tooling::FrontendActionFactory> Factory =
+ tooling::newFrontendActionFactory(&RenameAction);
+ int ExitCode;
+
+ if (Inplace) {
+ ExitCode = Tool.runAndSave(Factory.get());
+ } else {
+ ExitCode = Tool.run(Factory.get());
+
+ if (!ExportFixes.empty()) {
+ std::error_code EC;
+ llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::F_None);
+ if (EC) {
+ llvm::errs() << "Error opening output file: " << EC.message() << '\n';
+ exit(1);
+ }
+
+ // Export replacements.
+ tooling::TranslationUnitReplacements TUR;
+ const auto &FileToReplacements = Tool.getReplacements();
+ for (const auto &Entry : FileToReplacements)
+ TUR.Replacements.insert(TUR.Replacements.end(), Entry.second.begin(),
+ Entry.second.end());
+
+ yaml::Output YAML(OS);
+ YAML << TUR;
+ OS.close();
+ exit(0);
+ }
+
+ // Write every file to stdout. Right now we just barf the files without any
+ // indication of which files start where, other than that we print the files
+ // in the same order we see them.
+ LangOptions DefaultLangOptions;
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+ TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
+ DiagnosticsEngine Diagnostics(
+ IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
+ &DiagnosticPrinter, false);
+ auto &FileMgr = Tool.getFiles();
+ SourceManager Sources(Diagnostics, FileMgr);
+ Rewriter Rewrite(Sources, DefaultLangOptions);
+
+ Tool.applyAllReplacements(Rewrite);
+ for (const auto &File : Files) {
+ const auto *Entry = FileMgr.getFile(File);
+ const auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
+ Rewrite.getEditBuffer(ID).write(outs());
+ }
+ }
+
+ exit(ExitCode);
+}
diff --git a/clang/tools/clang-rename/clang-rename.el b/clang/tools/clang-rename/clang-rename.el
new file mode 100644
index 00000000000..b6c3ed4c686
--- /dev/null
+++ b/clang/tools/clang-rename/clang-rename.el
@@ -0,0 +1,79 @@
+;;; clang-rename.el --- Renames every occurrence of a symbol found at <offset>. -*- lexical-binding: t; -*-
+
+;; Keywords: tools, c
+
+;;; Commentary:
+
+;; To install clang-rename.el make sure the directory of this file is in your
+;; `load-path' and add
+;;
+;; (require 'clang-rename)
+;;
+;; to your .emacs configuration.
+
+;;; Code:
+
+(defgroup clang-rename nil
+ "Integration with clang-rename"
+ :group 'c)
+
+(defcustom clang-rename-binary "clang-rename"
+ "Path to clang-rename executable."
+ :type '(file :must-match t)
+ :group 'clang-rename)
+
+;;;###autoload
+(defun clang-rename (new-name)
+ "Rename all instances of the symbol at point to NEW-NAME using clang-rename."
+ (interactive "sEnter a new name: ")
+ (save-some-buffers :all)
+ ;; clang-rename should not be combined with other operations when undoing.
+ (undo-boundary)
+ (let ((output-buffer (get-buffer-create "*clang-rename*")))
+ (with-current-buffer output-buffer (erase-buffer))
+ (let ((exit-code (call-process
+ clang-rename-binary nil output-buffer nil
+ (format "-offset=%d"
+ ;; clang-rename wants file (byte) offsets, not
+ ;; buffer (character) positions.
+ (clang-rename--bufferpos-to-filepos
+ ;; Emacs treats one character after a symbol as
+ ;; part of the symbol, but clang-rename doesn’t.
+ ;; Use the beginning of the current symbol, if
+ ;; available, to resolve the inconsistency.
+ (or (car (bounds-of-thing-at-point 'symbol))
+ (point))
+ 'exact))
+ (format "-new-name=%s" new-name)
+ "-i" (buffer-file-name))))
+ (if (and (integerp exit-code) (zerop exit-code))
+ ;; Success; revert current buffer so it gets the modifications.
+ (progn
+ (kill-buffer output-buffer)
+ (revert-buffer :ignore-auto :noconfirm :preserve-modes))
+ ;; Failure; append exit code to output buffer and display it.
+ (let ((message (clang-rename--format-message
+ "clang-rename failed with %s %s"
+ (if (integerp exit-code) "exit status" "signal")
+ exit-code)))
+ (with-current-buffer output-buffer
+ (insert ?\n message ?\n))
+ (message "%s" message)
+ (display-buffer output-buffer))))))
+
+(defalias 'clang-rename--bufferpos-to-filepos
+ (if (fboundp 'bufferpos-to-filepos)
+ 'bufferpos-to-filepos
+ ;; Emacs 24 doesn’t have ‘bufferpos-to-filepos’, simulate it using
+ ;; ‘position-bytes’.
+ (lambda (position &optional _quality _coding-system)
+ (1- (position-bytes position)))))
+
+;; ‘format-message’ is new in Emacs 25.1. Provide a fallback for older
+;; versions.
+(defalias 'clang-rename--format-message
+ (if (fboundp 'format-message) 'format-message 'format))
+
+(provide 'clang-rename)
+
+;;; clang-rename.el ends here
diff --git a/clang/tools/clang-rename/clang-rename.py b/clang/tools/clang-rename/clang-rename.py
new file mode 100644
index 00000000000..3cc6644ff8f
--- /dev/null
+++ b/clang/tools/clang-rename/clang-rename.py
@@ -0,0 +1,61 @@
+'''
+Minimal clang-rename integration with Vim.
+
+Before installing make sure one of the following is satisfied:
+
+* clang-rename is in your PATH
+* `g:clang_rename_path` in ~/.vimrc points to valid clang-rename executable
+* `binary` in clang-rename.py points to valid to clang-rename executable
+
+To install, simply put this into your ~/.vimrc
+
+ noremap <leader>cr :pyf <path-to>/clang-rename.py<cr>
+
+IMPORTANT NOTE: Before running the tool, make sure you saved the file.
+
+All you have to do now is to place a cursor on a variable/function/class which
+you would like to rename and press '<leader>cr'. You will be prompted for a new
+name if the cursor points to a valid symbol.
+'''
+
+import vim
+import subprocess
+import sys
+
+def main():
+ binary = 'clang-rename'
+ if vim.eval('exists("g:clang_rename_path")') == "1":
+ binary = vim.eval('g:clang_rename_path')
+
+ # Get arguments for clang-rename binary.
+ offset = int(vim.eval('line2byte(line("."))+col(".")')) - 2
+ if offset < 0:
+ print >> sys.stderr, '''Couldn\'t determine cursor position.
+ Is your file empty?'''
+ return
+ filename = vim.current.buffer.name
+
+ new_name_request_message = 'type new name:'
+ new_name = vim.eval("input('{}\n')".format(new_name_request_message))
+
+ # Call clang-rename.
+ command = [binary,
+ filename,
+ '-i',
+ '-offset', str(offset),
+ '-new-name', str(new_name)]
+ # FIXME: make it possible to run the tool on unsaved file.
+ p = subprocess.Popen(command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+
+ if stderr:
+ print stderr
+
+ # Reload all buffers in Vim.
+ vim.command("checktime")
+
+
+if __name__ == '__main__':
+ main()
diff --git a/clang/unittests/CMakeLists.txt b/clang/unittests/CMakeLists.txt
index 7d407ce3f64..b622d66af4e 100644
--- a/clang/unittests/CMakeLists.txt
+++ b/clang/unittests/CMakeLists.txt
@@ -29,3 +29,4 @@ add_subdirectory(CodeGen)
if(NOT WIN32 AND CLANG_TOOL_LIBCLANG_BUILD)
add_subdirectory(libclang)
endif()
+add_subdirectory(Rename)
diff --git a/clang/unittests/Rename/CMakeLists.txt b/clang/unittests/Rename/CMakeLists.txt
new file mode 100644
index 00000000000..0b70eed8c9b
--- /dev/null
+++ b/clang/unittests/Rename/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+# We'd like clang/unittests/Tooling/RewriterTestContext.h in the test.
+include_directories(${CLANG_SOURCE_DIR})
+
+add_extra_unittest(ClangRenameTests
+ RenameClassTest.cpp
+ )
+
+target_link_libraries(ClangRenameTests
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangFormat
+ clangFrontend
+ clangRewrite
+ clangTooling
+ clangToolingCore
+ clangToolingRefactor
+ )
diff --git a/clang/unittests/Rename/ClangRenameTest.h b/clang/unittests/Rename/ClangRenameTest.h
new file mode 100644
index 00000000000..0933dd5aaf4
--- /dev/null
+++ b/clang/unittests/Rename/ClangRenameTest.h
@@ -0,0 +1,112 @@
+//===-- ClangRenameTests.cpp - clang-rename unit tests --------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#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/Refactoring/Rename/RenamingAction.h"
+#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "gtest/gtest.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace clang_rename {
+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(); }
+
+ std::string runClangRenameOnCode(llvm::StringRef Code,
+ llvm::StringRef OldName,
+ llvm::StringRef NewName) {
+ std::string NewCode;
+ llvm::raw_string_ostream(NewCode) << llvm::format(
+ "#include \"%s\"\n%s", HeaderName.c_str(), Code.str().c_str());
+ tooling::FileContentMappings FileContents = {{HeaderName, HeaderContent},
+ {CCName, NewCode}};
+ clang::RewriterTestContext Context;
+ Context.createInMemoryFile(HeaderName, HeaderContent);
+ clang::FileID InputFileID = Context.createInMemoryFile(CCName, NewCode);
+
+ tooling::USRFindingAction FindingAction({}, {OldName}, false);
+ std::unique_ptr<tooling::FrontendActionFactory> USRFindingActionFactory =
+ tooling::newFrontendActionFactory(&FindingAction);
+
+ if (!tooling::runToolOnCodeWithArgs(
+ USRFindingActionFactory->create(), NewCode, {"-std=c++11"}, CCName,
+ "clang-rename", std::make_shared<PCHContainerOperations>(),
+ FileContents))
+ return "";
+
+ const std::vector<std::vector<std::string>> &USRList =
+ FindingAction.getUSRList();
+ std::vector<std::string> NewNames = {NewName};
+ std::map<std::string, tooling::Replacements> FileToReplacements;
+ tooling::QualifiedRenamingAction RenameAction(NewNames, USRList,
+ FileToReplacements);
+ auto RenameActionFactory = tooling::newFrontendActionFactory(&RenameAction);
+ if (!tooling::runToolOnCodeWithArgs(
+ RenameActionFactory->create(), NewCode, {"-std=c++11"}, CCName,
+ "clang-rename", std::make_shared<PCHContainerOperations>(),
+ FileContents))
+ return "";
+
+ formatAndApplyAllReplacements(FileToReplacements, Context.Rewrite, "llvm");
+ return Context.getRewrittenText(InputFileID);
+ }
+
+ void CompareSnippets(StringRef Expected, StringRef Actual) {
+ std::string ExpectedCode;
+ llvm::raw_string_ostream(ExpectedCode) << llvm::format(
+ "#include \"%s\"\n%s", HeaderName.c_str(), Expected.str().c_str());
+ EXPECT_EQ(format(ExpectedCode), format(Actual));
+ }
+
+ 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;
+ }
+
+ std::string HeaderContent;
+ std::string HeaderName = "header.h";
+ std::string CCName = "input.cc";
+};
+
+} // namespace test
+} // namespace clang_rename
+} // namesdpace clang
diff --git a/clang/unittests/Rename/RenameClassTest.cpp b/clang/unittests/Rename/RenameClassTest.cpp
new file mode 100644
index 00000000000..29b4594fb0a
--- /dev/null
+++ b/clang/unittests/Rename/RenameClassTest.cpp
@@ -0,0 +1,684 @@
+//===-- 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