Coverage Report

Created: 2021-09-21 08:58

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/tools/clang-rename/ClangRename.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- tools/extra/clang-rename/ClangRename.cpp - Clang rename tool -----===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
///
9
/// \file
10
/// This file implements a clang-rename tool that automatically finds and
11
/// renames symbols in C++ code.
12
///
13
//===----------------------------------------------------------------------===//
14
15
#include "clang/Basic/Diagnostic.h"
16
#include "clang/Basic/DiagnosticOptions.h"
17
#include "clang/Basic/FileManager.h"
18
#include "clang/Basic/IdentifierTable.h"
19
#include "clang/Basic/LangOptions.h"
20
#include "clang/Basic/SourceManager.h"
21
#include "clang/Basic/TokenKinds.h"
22
#include "clang/Frontend/TextDiagnosticPrinter.h"
23
#include "clang/Rewrite/Core/Rewriter.h"
24
#include "clang/Tooling/CommonOptionsParser.h"
25
#include "clang/Tooling/Refactoring.h"
26
#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
27
#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
28
#include "clang/Tooling/ReplacementsYaml.h"
29
#include "clang/Tooling/Tooling.h"
30
#include "llvm/ADT/IntrusiveRefCntPtr.h"
31
#include "llvm/Support/CommandLine.h"
32
#include "llvm/Support/FileSystem.h"
33
#include "llvm/Support/YAMLTraits.h"
34
#include "llvm/Support/raw_ostream.h"
35
#include <string>
36
#include <system_error>
37
38
using namespace llvm;
39
using namespace clang;
40
41
/// An oldname -> newname rename.
42
struct RenameAllInfo {
43
  unsigned Offset = 0;
44
  std::string QualifiedName;
45
  std::string NewName;
46
};
47
48
LLVM_YAML_IS_SEQUENCE_VECTOR(RenameAllInfo)
49
50
namespace llvm {
51
namespace yaml {
52
53
/// Specialized MappingTraits to describe how a RenameAllInfo is
54
/// (de)serialized.
55
template <> struct MappingTraits<RenameAllInfo> {
56
4
  static void mapping(IO &IO, RenameAllInfo &Info) {
57
4
    IO.mapOptional("Offset", Info.Offset);
58
4
    IO.mapOptional("QualifiedName", Info.QualifiedName);
59
4
    IO.mapRequired("NewName", Info.NewName);
60
4
  }
61
};
62
63
} // end namespace yaml
64
} // end namespace llvm
65
66
static cl::OptionCategory ClangRenameOptions("clang-rename common options");
67
68
static cl::list<unsigned> SymbolOffsets(
69
    "offset",
70
    cl::desc("Locates the symbol by offset as opposed to <line>:<column>."),
71
    cl::ZeroOrMore, cl::cat(ClangRenameOptions));
72
static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited <file>s."),
73
                             cl::cat(ClangRenameOptions));
74
static cl::list<std::string>
75
    QualifiedNames("qualified-name",
76
                   cl::desc("The fully qualified name of the symbol."),
77
                   cl::ZeroOrMore, cl::cat(ClangRenameOptions));
78
79
static cl::list<std::string>
80
    NewNames("new-name", cl::desc("The new name to change the symbol to."),
81
             cl::ZeroOrMore, cl::cat(ClangRenameOptions));
82
static cl::opt<bool> PrintName(
83
    "pn",
84
    cl::desc("Print the found symbol's name prior to renaming to stderr."),
85
    cl::cat(ClangRenameOptions));
86
static cl::opt<bool> PrintLocations(
87
    "pl", cl::desc("Print the locations affected by renaming to stderr."),
88
    cl::cat(ClangRenameOptions));
89
static cl::opt<std::string>
90
    ExportFixes("export-fixes",
91
                cl::desc("YAML file to store suggested fixes in."),
92
                cl::value_desc("filename"), cl::cat(ClangRenameOptions));
93
static cl::opt<std::string>
94
    Input("input", cl::desc("YAML file to load oldname-newname pairs from."),
95
          cl::Optional, cl::cat(ClangRenameOptions));
96
static cl::opt<bool> Force("force",
97
                           cl::desc("Ignore nonexistent qualified names."),
98
                           cl::cat(ClangRenameOptions));
99
100
72
int main(int argc, const char **argv) {
101
72
  auto ExpectedParser =
102
72
      tooling::CommonOptionsParser::create(argc, argv, ClangRenameOptions);
103
72
  if (!ExpectedParser) {
104
0
    llvm::errs() << ExpectedParser.takeError();
105
0
    return 1;
106
0
  }
107
72
  tooling::CommonOptionsParser &OP = ExpectedParser.get();
108
109
72
  if (!Input.empty()) {
110
    // Populate QualifiedNames and NewNames from a YAML file.
111
2
    ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
112
2
        llvm::MemoryBuffer::getFile(Input);
113
2
    if (!Buffer) {
114
0
      errs() << "clang-rename: failed to read " << Input << ": "
115
0
             << Buffer.getError().message() << "\n";
116
0
      return 1;
117
0
    }
118
119
2
    std::vector<RenameAllInfo> Infos;
120
2
    llvm::yaml::Input YAML(Buffer.get()->getBuffer());
121
2
    YAML >> Infos;
122
4
    for (const auto &Info : Infos) {
123
4
      if (!Info.QualifiedName.empty())
124
2
        QualifiedNames.push_back(Info.QualifiedName);
125
2
      else
126
2
        SymbolOffsets.push_back(Info.Offset);
127
4
      NewNames.push_back(Info.NewName);
128
4
    }
129
2
  }
130
131
  // Check the arguments for correctness.
132
72
  if (NewNames.empty()) {
133
1
    errs() << "clang-rename: -new-name must be specified.\n\n";
134
1
    return 1;
135
1
  }
136
137
71
  if (SymbolOffsets.empty() == QualifiedNames.empty()) {
138
0
    errs() << "clang-rename: -offset and -qualified-name can't be present at "
139
0
              "the same time.\n";
140
0
    return 1;
141
0
  }
142
143
  // Check if NewNames is a valid identifier in C++17.
144
71
  LangOptions Options;
145
71
  Options.CPlusPlus = true;
146
71
  Options.CPlusPlus17 = true;
147
71
  IdentifierTable Table(Options);
148
76
  for (const auto &NewName : NewNames) {
149
76
    auto NewNameTokKind = Table.get(NewName).getTokenID();
150
76
    if (!tok::isAnyIdentifier(NewNameTokKind)) {
151
1
      errs() << "ERROR: new name is not a valid identifier in C++17.\n\n";
152
1
      return 1;
153
1
    }
154
76
  }
155
156
70
  if (SymbolOffsets.size() + QualifiedNames.size() != NewNames.size()) {
157
0
    errs() << "clang-rename: number of symbol offsets(" << SymbolOffsets.size()
158
0
           << ") + number of qualified names (" << QualifiedNames.size()
159
0
           << ") must be equal to number of new names(" << NewNames.size()
160
0
           << ").\n\n";
161
0
    cl::PrintHelpMessage();
162
0
    return 1;
163
0
  }
164
165
70
  auto Files = OP.getSourcePathList();
166
70
  tooling::RefactoringTool Tool(OP.getCompilations(), Files);
167
70
  tooling::USRFindingAction FindingAction(SymbolOffsets, QualifiedNames, Force);
168
70
  Tool.run(tooling::newFrontendActionFactory(&FindingAction).get());
169
70
  const std::vector<std::vector<std::string>> &USRList =
170
70
      FindingAction.getUSRList();
171
70
  const std::vector<std::string> &PrevNames = FindingAction.getUSRSpellings();
172
70
  if (PrintName) {
173
0
    for (const auto &PrevName : PrevNames) {
174
0
      outs() << "clang-rename found name: " << PrevName << '\n';
175
0
    }
176
0
  }
177
178
70
  if (FindingAction.errorOccurred()) {
179
    // Diagnostics are already issued at this point.
180
1
    return 1;
181
1
  }
182
183
  // Perform the renaming.
184
69
  tooling::RenamingAction RenameAction(NewNames, PrevNames, USRList,
185
69
                                       Tool.getReplacements(), PrintLocations);
186
69
  std::unique_ptr<tooling::FrontendActionFactory> Factory =
187
69
      tooling::newFrontendActionFactory(&RenameAction);
188
69
  int ExitCode;
189
190
69
  if (Inplace) {
191
0
    ExitCode = Tool.runAndSave(Factory.get());
192
69
  } else {
193
69
    ExitCode = Tool.run(Factory.get());
194
195
69
    if (!ExportFixes.empty()) {
196
0
      std::error_code EC;
197
0
      llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::OF_None);
198
0
      if (EC) {
199
0
        llvm::errs() << "Error opening output file: " << EC.message() << '\n';
200
0
        return 1;
201
0
      }
202
203
      // Export replacements.
204
0
      tooling::TranslationUnitReplacements TUR;
205
0
      const auto &FileToReplacements = Tool.getReplacements();
206
0
      for (const auto &Entry : FileToReplacements)
207
0
        TUR.Replacements.insert(TUR.Replacements.end(), Entry.second.begin(),
208
0
                                Entry.second.end());
209
210
0
      yaml::Output YAML(OS);
211
0
      YAML << TUR;
212
0
      OS.close();
213
0
      return 0;
214
0
    }
215
216
    // Write every file to stdout. Right now we just barf the files without any
217
    // indication of which files start where, other than that we print the files
218
    // in the same order we see them.
219
69
    LangOptions DefaultLangOptions;
220
69
    IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
221
69
    TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
222
69
    DiagnosticsEngine Diagnostics(
223
69
        IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
224
69
        &DiagnosticPrinter, false);
225
69
    auto &FileMgr = Tool.getFiles();
226
69
    SourceManager Sources(Diagnostics, FileMgr);
227
69
    Rewriter Rewrite(Sources, DefaultLangOptions);
228
229
69
    Tool.applyAllReplacements(Rewrite);
230
69
    for (const auto &File : Files) {
231
69
      auto Entry = FileMgr.getFile(File);
232
69
      const auto ID = Sources.getOrCreateFileID(*Entry, SrcMgr::C_User);
233
69
      Rewrite.getEditBuffer(ID).write(outs());
234
69
    }
235
69
  }
236
237
69
  return ExitCode;
238
69
}