123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- //===--- tools/extra/clang-rename/ClangRename.cpp - Clang rename tool -----===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- ///
- /// \file
- /// 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 <string>
- #include <system_error>
- using namespace llvm;
- using namespace clang;
- /// 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 {
- /// 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) {
- auto ExpectedParser =
- tooling::CommonOptionsParser::create(argc, argv, ClangRenameOptions);
- if (!ExpectedParser) {
- llvm::errs() << ExpectedParser.takeError();
- return 1;
- }
- tooling::CommonOptionsParser &OP = ExpectedParser.get();
- 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";
- return 1;
- }
- if (SymbolOffsets.empty() == QualifiedNames.empty()) {
- errs() << "clang-rename: -offset and -qualified-name can't be present at "
- "the same time.\n";
- return 1;
- }
- // Check if NewNames is a valid identifier in C++17.
- LangOptions Options;
- Options.CPlusPlus = true;
- Options.CPlusPlus17 = 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";
- return 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();
- return 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.
- return 1;
- }
- // 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::OF_None);
- if (EC) {
- llvm::errs() << "Error opening output file: " << EC.message() << '\n';
- return 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();
- return 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) {
- auto Entry = FileMgr.getFile(File);
- const auto ID = Sources.getOrCreateFileID(*Entry, SrcMgr::C_User);
- Rewrite.getEditBuffer(ID).write(outs());
- }
- }
- return ExitCode;
- }
|