xref: /netbsd-src/external/apache2/llvm/dist/clang/tools/clang-rename/ClangRename.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //===--- tools/extra/clang-rename/ClangRename.cpp - Clang rename tool -----===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg ///
97330f729Sjoerg /// \file
107330f729Sjoerg /// This file implements a clang-rename tool that automatically finds and
117330f729Sjoerg /// renames symbols in C++ code.
127330f729Sjoerg ///
137330f729Sjoerg //===----------------------------------------------------------------------===//
147330f729Sjoerg 
157330f729Sjoerg #include "clang/Basic/Diagnostic.h"
167330f729Sjoerg #include "clang/Basic/DiagnosticOptions.h"
177330f729Sjoerg #include "clang/Basic/FileManager.h"
187330f729Sjoerg #include "clang/Basic/IdentifierTable.h"
197330f729Sjoerg #include "clang/Basic/LangOptions.h"
207330f729Sjoerg #include "clang/Basic/SourceManager.h"
217330f729Sjoerg #include "clang/Basic/TokenKinds.h"
227330f729Sjoerg #include "clang/Frontend/TextDiagnosticPrinter.h"
237330f729Sjoerg #include "clang/Rewrite/Core/Rewriter.h"
247330f729Sjoerg #include "clang/Tooling/CommonOptionsParser.h"
257330f729Sjoerg #include "clang/Tooling/Refactoring.h"
267330f729Sjoerg #include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
277330f729Sjoerg #include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
287330f729Sjoerg #include "clang/Tooling/ReplacementsYaml.h"
297330f729Sjoerg #include "clang/Tooling/Tooling.h"
307330f729Sjoerg #include "llvm/ADT/IntrusiveRefCntPtr.h"
317330f729Sjoerg #include "llvm/Support/CommandLine.h"
327330f729Sjoerg #include "llvm/Support/FileSystem.h"
337330f729Sjoerg #include "llvm/Support/YAMLTraits.h"
347330f729Sjoerg #include "llvm/Support/raw_ostream.h"
357330f729Sjoerg #include <string>
367330f729Sjoerg #include <system_error>
377330f729Sjoerg 
387330f729Sjoerg using namespace llvm;
397330f729Sjoerg using namespace clang;
407330f729Sjoerg 
417330f729Sjoerg /// An oldname -> newname rename.
427330f729Sjoerg struct RenameAllInfo {
437330f729Sjoerg   unsigned Offset = 0;
447330f729Sjoerg   std::string QualifiedName;
457330f729Sjoerg   std::string NewName;
467330f729Sjoerg };
477330f729Sjoerg 
487330f729Sjoerg LLVM_YAML_IS_SEQUENCE_VECTOR(RenameAllInfo)
497330f729Sjoerg 
507330f729Sjoerg namespace llvm {
517330f729Sjoerg namespace yaml {
527330f729Sjoerg 
537330f729Sjoerg /// Specialized MappingTraits to describe how a RenameAllInfo is
547330f729Sjoerg /// (de)serialized.
557330f729Sjoerg template <> struct MappingTraits<RenameAllInfo> {
mappingllvm::yaml::MappingTraits567330f729Sjoerg   static void mapping(IO &IO, RenameAllInfo &Info) {
577330f729Sjoerg     IO.mapOptional("Offset", Info.Offset);
587330f729Sjoerg     IO.mapOptional("QualifiedName", Info.QualifiedName);
597330f729Sjoerg     IO.mapRequired("NewName", Info.NewName);
607330f729Sjoerg   }
617330f729Sjoerg };
627330f729Sjoerg 
637330f729Sjoerg } // end namespace yaml
647330f729Sjoerg } // end namespace llvm
657330f729Sjoerg 
667330f729Sjoerg static cl::OptionCategory ClangRenameOptions("clang-rename common options");
677330f729Sjoerg 
687330f729Sjoerg static cl::list<unsigned> SymbolOffsets(
697330f729Sjoerg     "offset",
707330f729Sjoerg     cl::desc("Locates the symbol by offset as opposed to <line>:<column>."),
717330f729Sjoerg     cl::ZeroOrMore, cl::cat(ClangRenameOptions));
727330f729Sjoerg static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited <file>s."),
737330f729Sjoerg                              cl::cat(ClangRenameOptions));
747330f729Sjoerg static cl::list<std::string>
757330f729Sjoerg     QualifiedNames("qualified-name",
767330f729Sjoerg                    cl::desc("The fully qualified name of the symbol."),
777330f729Sjoerg                    cl::ZeroOrMore, cl::cat(ClangRenameOptions));
787330f729Sjoerg 
797330f729Sjoerg static cl::list<std::string>
807330f729Sjoerg     NewNames("new-name", cl::desc("The new name to change the symbol to."),
817330f729Sjoerg              cl::ZeroOrMore, cl::cat(ClangRenameOptions));
827330f729Sjoerg static cl::opt<bool> PrintName(
837330f729Sjoerg     "pn",
847330f729Sjoerg     cl::desc("Print the found symbol's name prior to renaming to stderr."),
857330f729Sjoerg     cl::cat(ClangRenameOptions));
867330f729Sjoerg static cl::opt<bool> PrintLocations(
877330f729Sjoerg     "pl", cl::desc("Print the locations affected by renaming to stderr."),
887330f729Sjoerg     cl::cat(ClangRenameOptions));
897330f729Sjoerg static cl::opt<std::string>
907330f729Sjoerg     ExportFixes("export-fixes",
917330f729Sjoerg                 cl::desc("YAML file to store suggested fixes in."),
927330f729Sjoerg                 cl::value_desc("filename"), cl::cat(ClangRenameOptions));
937330f729Sjoerg static cl::opt<std::string>
947330f729Sjoerg     Input("input", cl::desc("YAML file to load oldname-newname pairs from."),
957330f729Sjoerg           cl::Optional, cl::cat(ClangRenameOptions));
967330f729Sjoerg static cl::opt<bool> Force("force",
977330f729Sjoerg                            cl::desc("Ignore nonexistent qualified names."),
987330f729Sjoerg                            cl::cat(ClangRenameOptions));
997330f729Sjoerg 
main(int argc,const char ** argv)1007330f729Sjoerg int main(int argc, const char **argv) {
101*e038c9c4Sjoerg   auto ExpectedParser =
102*e038c9c4Sjoerg       tooling::CommonOptionsParser::create(argc, argv, ClangRenameOptions);
103*e038c9c4Sjoerg   if (!ExpectedParser) {
104*e038c9c4Sjoerg     llvm::errs() << ExpectedParser.takeError();
105*e038c9c4Sjoerg     return 1;
106*e038c9c4Sjoerg   }
107*e038c9c4Sjoerg   tooling::CommonOptionsParser &OP = ExpectedParser.get();
1087330f729Sjoerg 
1097330f729Sjoerg   if (!Input.empty()) {
1107330f729Sjoerg     // Populate QualifiedNames and NewNames from a YAML file.
1117330f729Sjoerg     ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
1127330f729Sjoerg         llvm::MemoryBuffer::getFile(Input);
1137330f729Sjoerg     if (!Buffer) {
1147330f729Sjoerg       errs() << "clang-rename: failed to read " << Input << ": "
1157330f729Sjoerg              << Buffer.getError().message() << "\n";
1167330f729Sjoerg       return 1;
1177330f729Sjoerg     }
1187330f729Sjoerg 
1197330f729Sjoerg     std::vector<RenameAllInfo> Infos;
1207330f729Sjoerg     llvm::yaml::Input YAML(Buffer.get()->getBuffer());
1217330f729Sjoerg     YAML >> Infos;
1227330f729Sjoerg     for (const auto &Info : Infos) {
1237330f729Sjoerg       if (!Info.QualifiedName.empty())
1247330f729Sjoerg         QualifiedNames.push_back(Info.QualifiedName);
1257330f729Sjoerg       else
1267330f729Sjoerg         SymbolOffsets.push_back(Info.Offset);
1277330f729Sjoerg       NewNames.push_back(Info.NewName);
1287330f729Sjoerg     }
1297330f729Sjoerg   }
1307330f729Sjoerg 
1317330f729Sjoerg   // Check the arguments for correctness.
1327330f729Sjoerg   if (NewNames.empty()) {
1337330f729Sjoerg     errs() << "clang-rename: -new-name must be specified.\n\n";
1347330f729Sjoerg     return 1;
1357330f729Sjoerg   }
1367330f729Sjoerg 
1377330f729Sjoerg   if (SymbolOffsets.empty() == QualifiedNames.empty()) {
1387330f729Sjoerg     errs() << "clang-rename: -offset and -qualified-name can't be present at "
1397330f729Sjoerg               "the same time.\n";
1407330f729Sjoerg     return 1;
1417330f729Sjoerg   }
1427330f729Sjoerg 
1437330f729Sjoerg   // Check if NewNames is a valid identifier in C++17.
1447330f729Sjoerg   LangOptions Options;
1457330f729Sjoerg   Options.CPlusPlus = true;
1467330f729Sjoerg   Options.CPlusPlus17 = true;
1477330f729Sjoerg   IdentifierTable Table(Options);
1487330f729Sjoerg   for (const auto &NewName : NewNames) {
1497330f729Sjoerg     auto NewNameTokKind = Table.get(NewName).getTokenID();
1507330f729Sjoerg     if (!tok::isAnyIdentifier(NewNameTokKind)) {
1517330f729Sjoerg       errs() << "ERROR: new name is not a valid identifier in C++17.\n\n";
1527330f729Sjoerg       return 1;
1537330f729Sjoerg     }
1547330f729Sjoerg   }
1557330f729Sjoerg 
1567330f729Sjoerg   if (SymbolOffsets.size() + QualifiedNames.size() != NewNames.size()) {
1577330f729Sjoerg     errs() << "clang-rename: number of symbol offsets(" << SymbolOffsets.size()
1587330f729Sjoerg            << ") + number of qualified names (" << QualifiedNames.size()
1597330f729Sjoerg            << ") must be equal to number of new names(" << NewNames.size()
1607330f729Sjoerg            << ").\n\n";
1617330f729Sjoerg     cl::PrintHelpMessage();
1627330f729Sjoerg     return 1;
1637330f729Sjoerg   }
1647330f729Sjoerg 
1657330f729Sjoerg   auto Files = OP.getSourcePathList();
1667330f729Sjoerg   tooling::RefactoringTool Tool(OP.getCompilations(), Files);
1677330f729Sjoerg   tooling::USRFindingAction FindingAction(SymbolOffsets, QualifiedNames, Force);
1687330f729Sjoerg   Tool.run(tooling::newFrontendActionFactory(&FindingAction).get());
1697330f729Sjoerg   const std::vector<std::vector<std::string>> &USRList =
1707330f729Sjoerg       FindingAction.getUSRList();
1717330f729Sjoerg   const std::vector<std::string> &PrevNames = FindingAction.getUSRSpellings();
1727330f729Sjoerg   if (PrintName) {
1737330f729Sjoerg     for (const auto &PrevName : PrevNames) {
1747330f729Sjoerg       outs() << "clang-rename found name: " << PrevName << '\n';
1757330f729Sjoerg     }
1767330f729Sjoerg   }
1777330f729Sjoerg 
1787330f729Sjoerg   if (FindingAction.errorOccurred()) {
1797330f729Sjoerg     // Diagnostics are already issued at this point.
1807330f729Sjoerg     return 1;
1817330f729Sjoerg   }
1827330f729Sjoerg 
1837330f729Sjoerg   // Perform the renaming.
1847330f729Sjoerg   tooling::RenamingAction RenameAction(NewNames, PrevNames, USRList,
1857330f729Sjoerg                                        Tool.getReplacements(), PrintLocations);
1867330f729Sjoerg   std::unique_ptr<tooling::FrontendActionFactory> Factory =
1877330f729Sjoerg       tooling::newFrontendActionFactory(&RenameAction);
1887330f729Sjoerg   int ExitCode;
1897330f729Sjoerg 
1907330f729Sjoerg   if (Inplace) {
1917330f729Sjoerg     ExitCode = Tool.runAndSave(Factory.get());
1927330f729Sjoerg   } else {
1937330f729Sjoerg     ExitCode = Tool.run(Factory.get());
1947330f729Sjoerg 
1957330f729Sjoerg     if (!ExportFixes.empty()) {
1967330f729Sjoerg       std::error_code EC;
1977330f729Sjoerg       llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::OF_None);
1987330f729Sjoerg       if (EC) {
1997330f729Sjoerg         llvm::errs() << "Error opening output file: " << EC.message() << '\n';
2007330f729Sjoerg         return 1;
2017330f729Sjoerg       }
2027330f729Sjoerg 
2037330f729Sjoerg       // Export replacements.
2047330f729Sjoerg       tooling::TranslationUnitReplacements TUR;
2057330f729Sjoerg       const auto &FileToReplacements = Tool.getReplacements();
2067330f729Sjoerg       for (const auto &Entry : FileToReplacements)
2077330f729Sjoerg         TUR.Replacements.insert(TUR.Replacements.end(), Entry.second.begin(),
2087330f729Sjoerg                                 Entry.second.end());
2097330f729Sjoerg 
2107330f729Sjoerg       yaml::Output YAML(OS);
2117330f729Sjoerg       YAML << TUR;
2127330f729Sjoerg       OS.close();
2137330f729Sjoerg       return 0;
2147330f729Sjoerg     }
2157330f729Sjoerg 
2167330f729Sjoerg     // Write every file to stdout. Right now we just barf the files without any
2177330f729Sjoerg     // indication of which files start where, other than that we print the files
2187330f729Sjoerg     // in the same order we see them.
2197330f729Sjoerg     LangOptions DefaultLangOptions;
2207330f729Sjoerg     IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
2217330f729Sjoerg     TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
2227330f729Sjoerg     DiagnosticsEngine Diagnostics(
2237330f729Sjoerg         IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
2247330f729Sjoerg         &DiagnosticPrinter, false);
2257330f729Sjoerg     auto &FileMgr = Tool.getFiles();
2267330f729Sjoerg     SourceManager Sources(Diagnostics, FileMgr);
2277330f729Sjoerg     Rewriter Rewrite(Sources, DefaultLangOptions);
2287330f729Sjoerg 
2297330f729Sjoerg     Tool.applyAllReplacements(Rewrite);
2307330f729Sjoerg     for (const auto &File : Files) {
2317330f729Sjoerg       auto Entry = FileMgr.getFile(File);
2327330f729Sjoerg       const auto ID = Sources.getOrCreateFileID(*Entry, SrcMgr::C_User);
2337330f729Sjoerg       Rewrite.getEditBuffer(ID).write(outs());
2347330f729Sjoerg     }
2357330f729Sjoerg   }
2367330f729Sjoerg 
2377330f729Sjoerg   return ExitCode;
2387330f729Sjoerg }
239