[clang-rename] Support renaming qualified symbol
Summary:
The patch adds a new feature for renaming qualified symbol references.
Unlike orginal clang-rename behavior, when renaming a qualified symbol to a new
qualified symbol (e.g "A::Foo" => "B::Bar"), this new rename behavior will
consider the prefix qualifiers of the symbol, and calculate the new prefix
qualifiers. It aims to add as few additional qualifiers as possible.
As this is an early version (only supports renaming classes), I don't change
current clang-rename interfaces at the moment, and would like to keep its
(command-line tool) behavior. So I added new interfaces for the prototype.
In the long run, these interfaces should be unified.
No functionality changes in original clang-rename command-line tool.
This patch also contains a few bug fixes of clang-rename which are discovered by
the new unittest:
* fix a potential nullptr accessment when class declaration doesn't have definition.
* add USRs of nested declartaions in "getNamedDeclFor".
Reviewers: ioeric
Reviewed By: ioeric
Subscribers: alexfh, cfe-commits, mgorny
Differential Revision: https://reviews.llvm.org/D31176
llvm-svn: 299419
diff --git a/clang-tools-extra/unittests/clang-rename/ClangRenameTest.h b/clang-tools-extra/unittests/clang-rename/ClangRenameTest.h
new file mode 100644
index 0000000..fd7f37a
--- /dev/null
+++ b/clang-tools-extra/unittests/clang-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 "RenamingAction.h"
+#include "USRFindingAction.h"
+#include "unittests/Tooling/RewriterTestContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/FileSystemOptions.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "clang/Format/Format.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/PCHContainerOperations.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/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);
+
+ rename::USRFindingAction FindingAction({}, {OldName});
+ 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;
+ rename::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