Coverage Report

Created: 2021-01-23 06:44

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/tools/arcmt-test/arcmt-test.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- arcmt-test.cpp - ARC Migration Tool testbed -----------------------===//
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
#include "clang/ARCMigrate/ARCMT.h"
10
#include "clang/AST/ASTContext.h"
11
#include "clang/Frontend/PCHContainerOperations.h"
12
#include "clang/Frontend/TextDiagnosticPrinter.h"
13
#include "clang/Frontend/Utils.h"
14
#include "clang/Frontend/VerifyDiagnosticConsumer.h"
15
#include "clang/Lex/Preprocessor.h"
16
#include "clang/Lex/PreprocessorOptions.h"
17
#include "llvm/Support/FileSystem.h"
18
#include "llvm/Support/MemoryBuffer.h"
19
#include "llvm/Support/Path.h"
20
#include "llvm/Support/Signals.h"
21
#include <system_error>
22
23
using namespace clang;
24
using namespace arcmt;
25
26
static llvm::cl::opt<bool>
27
CheckOnly("check-only",
28
      llvm::cl::desc("Just check for issues that need to be handled manually"));
29
30
//static llvm::cl::opt<bool>
31
//TestResultForARC("test-result",
32
//llvm::cl::desc("Test the result of transformations by parsing it in ARC mode"));
33
34
static llvm::cl::opt<bool>
35
OutputTransformations("output-transformations",
36
                      llvm::cl::desc("Print the source transformations"));
37
38
static llvm::cl::opt<bool>
39
VerifyDiags("verify",llvm::cl::desc("Verify emitted diagnostics and warnings"));
40
41
static llvm::cl::opt<bool>
42
VerboseOpt("v", llvm::cl::desc("Enable verbose output"));
43
44
static llvm::cl::opt<bool>
45
VerifyTransformedFiles("verify-transformed-files",
46
llvm::cl::desc("Read pairs of file mappings (typically the output of "
47
               "c-arcmt-test) and compare their contents with the filenames "
48
               "provided in command-line"));
49
50
static llvm::cl::opt<std::string>
51
RemappingsFile("remappings-file",
52
               llvm::cl::desc("Pairs of file mappings (typically the output of "
53
               "c-arcmt-test)"));
54
55
static llvm::cl::list<std::string>
56
ResultFiles(llvm::cl::Positional, llvm::cl::desc("<filename>..."));
57
58
static llvm::cl::extrahelp extraHelp(
59
  "\nusage with compiler args: arcmt-test [options] --args [compiler flags]\n");
60
61
// This function isn't referenced outside its translation unit, but it
62
// can't use the "static" keyword because its address is used for
63
// GetMainExecutable (since some platforms don't support taking the
64
// address of main, and some platforms can't implement GetMainExecutable
65
// without being given the address of a function in the main executable).
66
19
std::string GetExecutablePath(const char *Argv0) {
67
  // This just needs to be some symbol in the binary; C++ doesn't
68
  // allow taking the address of ::main however.
69
19
  void *MainAddr = (void*) (intptr_t) GetExecutablePath;
70
19
  return llvm::sys::fs::getMainExecutable(Argv0, MainAddr);
71
19
}
72
73
static void printSourceLocation(SourceLocation loc, ASTContext &Ctx,
74
                                raw_ostream &OS);
75
static void printSourceRange(CharSourceRange range, ASTContext &Ctx,
76
                             raw_ostream &OS);
77
78
namespace {
79
80
class PrintTransforms : public MigrationProcess::RewriteListener {
81
  ASTContext *Ctx;
82
  raw_ostream &OS;
83
84
public:
85
  PrintTransforms(raw_ostream &OS)
86
0
    : Ctx(nullptr), OS(OS) {}
87
88
0
  void start(ASTContext &ctx) override { Ctx = &ctx; }
89
0
  void finish() override { Ctx = nullptr; }
90
91
0
  void insert(SourceLocation loc, StringRef text) override {
92
0
    assert(Ctx);
93
0
    OS << "Insert: ";
94
0
    printSourceLocation(loc, *Ctx, OS);
95
0
    OS << " \"" << text << "\"\n";
96
0
  }
97
98
0
  void remove(CharSourceRange range) override {
99
0
    assert(Ctx);
100
0
    OS << "Remove: ";
101
0
    printSourceRange(range, *Ctx, OS);
102
0
    OS << '\n';
103
0
  }
104
};
105
106
} // anonymous namespace
107
108
static bool checkForMigration(StringRef resourcesPath,
109
26
                              ArrayRef<const char *> Args) {
110
26
  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
111
26
  DiagnosticConsumer *DiagClient =
112
26
    new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
113
26
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
114
26
  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
115
26
      new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
116
  // Chain in -verify checker, if requested.
117
26
  VerifyDiagnosticConsumer *verifyDiag = nullptr;
118
26
  if (VerifyDiags) {
119
0
    verifyDiag = new VerifyDiagnosticConsumer(*Diags);
120
0
    Diags->setClient(verifyDiag);
121
0
  }
122
123
26
  CompilerInvocation CI;
124
26
  if (!CompilerInvocation::CreateFromArgs(CI, Args, *Diags))
125
0
    return true;
126
127
26
  if (CI.getFrontendOpts().Inputs.empty()) {
128
0
    llvm::errs() << "error: no input files\n";
129
0
    return true;
130
0
  }
131
132
26
  if (!CI.getLangOpts()->ObjC)
133
0
    return false;
134
135
26
  arcmt::checkForManualIssues(CI, CI.getFrontendOpts().Inputs[0],
136
26
                              std::make_shared<PCHContainerOperations>(),
137
26
                              Diags->getClient());
138
26
  return Diags->getClient()->getNumErrors() > 0;
139
26
}
140
141
25
static void printResult(FileRemapper &remapper, raw_ostream &OS) {
142
0
  remapper.forEachMapping([](StringRef, StringRef) {},
143
25
                          [&](StringRef, const llvm::MemoryBufferRef &Buffer) {
144
25
                            OS << Buffer.getBuffer();
145
25
                          });
146
25
}
147
148
static bool performTransformations(StringRef resourcesPath,
149
26
                                   ArrayRef<const char *> Args) {
150
  // Check first.
151
26
  if (checkForMigration(resourcesPath, Args))
152
1
    return true;
153
154
25
  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
155
25
  DiagnosticConsumer *DiagClient =
156
25
    new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
157
25
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
158
25
  IntrusiveRefCntPtr<DiagnosticsEngine> TopDiags(
159
25
      new DiagnosticsEngine(DiagID, &*DiagOpts, &*DiagClient));
160
161
25
  CompilerInvocation origCI;
162
25
  if (!CompilerInvocation::CreateFromArgs(origCI, Args, *TopDiags))
163
0
    return true;
164
165
25
  if (origCI.getFrontendOpts().Inputs.empty()) {
166
0
    llvm::errs() << "error: no input files\n";
167
0
    return true;
168
0
  }
169
170
25
  if (!origCI.getLangOpts()->ObjC)
171
0
    return false;
172
173
25
  MigrationProcess migration(origCI, std::make_shared<PCHContainerOperations>(),
174
25
                             DiagClient);
175
176
25
  std::vector<TransformFn>
177
25
    transforms = arcmt::getAllTransformations(origCI.getLangOpts()->getGC(),
178
25
                                 origCI.getMigratorOpts().NoFinalizeRemoval);
179
25
  assert(!transforms.empty());
180
181
25
  std::unique_ptr<PrintTransforms> transformPrinter;
182
25
  if (OutputTransformations)
183
0
    transformPrinter.reset(new PrintTransforms(llvm::outs()));
184
185
77
  for (unsigned i=0, e = transforms.size(); i != e; 
++i52
) {
186
52
    bool err = migration.applyTransform(transforms[i], transformPrinter.get());
187
52
    if (err) 
return true0
;
188
189
52
    if (VerboseOpt) {
190
0
      if (i == e-1)
191
0
        llvm::errs() << "\n##### FINAL RESULT #####\n";
192
0
      else
193
0
        llvm::errs() << "\n##### OUTPUT AFTER "<< i+1 <<". TRANSFORMATION #####\n";
194
0
      printResult(migration.getRemapper(), llvm::errs());
195
0
      llvm::errs() << "\n##########################\n\n";
196
0
    }
197
52
  }
198
199
25
  if (!OutputTransformations)
200
25
    printResult(migration.getRemapper(), llvm::outs());
201
202
  // FIXME: TestResultForARC
203
204
25
  return false;
205
25
}
206
207
39
static bool filesCompareEqual(StringRef fname1, StringRef fname2) {
208
39
  using namespace llvm;
209
210
39
  ErrorOr<std::unique_ptr<MemoryBuffer>> file1 = MemoryBuffer::getFile(fname1);
211
39
  if (!file1)
212
0
    return false;
213
214
39
  ErrorOr<std::unique_ptr<MemoryBuffer>> file2 = MemoryBuffer::getFile(fname2);
215
39
  if (!file2)
216
0
    return false;
217
218
39
  return file1.get()->getBuffer() == file2.get()->getBuffer();
219
39
}
220
221
30
static bool verifyTransformedFiles(ArrayRef<std::string> resultFiles) {
222
30
  using namespace llvm;
223
224
30
  assert(!resultFiles.empty());
225
226
30
  std::map<StringRef, StringRef> resultMap;
227
228
30
  for (ArrayRef<std::string>::iterator
229
69
         I = resultFiles.begin(), E = resultFiles.end(); I != E; 
++I39
) {
230
39
    StringRef fname(*I);
231
39
    if (!fname.endswith(".result")) {
232
0
      errs() << "error: filename '" << fname
233
0
                   << "' does not have '.result' extension\n";
234
0
      return true;
235
0
    }
236
39
    resultMap[sys::path::stem(fname)] = fname;
237
39
  }
238
239
30
  ErrorOr<std::unique_ptr<MemoryBuffer>> inputBuf = std::error_code();
240
30
  if (RemappingsFile.empty())
241
30
    inputBuf = MemoryBuffer::getSTDIN();
242
0
  else
243
0
    inputBuf = MemoryBuffer::getFile(RemappingsFile);
244
30
  if (!inputBuf) {
245
0
    errs() << "error: could not read remappings input\n";
246
0
    return true;
247
0
  }
248
249
30
  SmallVector<StringRef, 8> strs;
250
30
  inputBuf.get()->getBuffer().split(strs, "\n", /*MaxSplit=*/-1,
251
30
                                    /*KeepEmpty=*/false);
252
253
30
  if (strs.empty()) {
254
0
    errs() << "error: no files to verify from stdin\n";
255
0
    return true;
256
0
  }
257
30
  if (strs.size() % 2 != 0) {
258
0
    errs() << "error: files to verify are not original/result pairs\n";
259
0
    return true;
260
0
  }
261
262
69
  
for (unsigned i = 0, e = strs.size(); 30
i != e;
i += 239
) {
263
39
    StringRef inputOrigFname = strs[i];
264
39
    StringRef inputResultFname = strs[i+1];
265
266
39
    std::map<StringRef, StringRef>::iterator It;
267
39
    It = resultMap.find(sys::path::filename(inputOrigFname));
268
39
    if (It == resultMap.end()) {
269
0
      errs() << "error: '" << inputOrigFname << "' is not in the list of "
270
0
             << "transformed files to verify\n";
271
0
      return true;
272
0
    }
273
274
39
    if (!sys::fs::exists(It->second)) {
275
0
      errs() << "error: '" << It->second << "' does not exist\n";
276
0
      return true;
277
0
    }
278
39
    if (!sys::fs::exists(inputResultFname)) {
279
0
      errs() << "error: '" << inputResultFname << "' does not exist\n";
280
0
      return true;
281
0
    }
282
283
39
    if (!filesCompareEqual(It->second, inputResultFname)) {
284
0
      errs() << "error: '" << It->second << "' is different than "
285
0
             << "'" << inputResultFname << "'\n";
286
0
      return true;
287
0
    }
288
289
39
    resultMap.erase(It);
290
39
  }
291
292
30
  if (!resultMap.empty()) {
293
0
    for (std::map<StringRef, StringRef>::iterator
294
0
           I = resultMap.begin(), E = resultMap.end(); I != E; ++I)
295
0
      errs() << "error: '" << I->second << "' was not verified!\n";
296
0
    return true;
297
0
  }
298
299
30
  return false; 
300
30
}
301
302
//===----------------------------------------------------------------------===//
303
// Misc. functions.
304
//===----------------------------------------------------------------------===//
305
306
static void printSourceLocation(SourceLocation loc, ASTContext &Ctx,
307
0
                                raw_ostream &OS) {
308
0
  SourceManager &SM = Ctx.getSourceManager();
309
0
  PresumedLoc PL = SM.getPresumedLoc(loc);
310
311
0
  OS << llvm::sys::path::filename(PL.getFilename());
312
0
  OS << ":" << PL.getLine() << ":"
313
0
            << PL.getColumn();
314
0
}
315
316
static void printSourceRange(CharSourceRange range, ASTContext &Ctx,
317
0
                             raw_ostream &OS) {
318
0
  SourceManager &SM = Ctx.getSourceManager();
319
0
  const LangOptions &langOpts = Ctx.getLangOpts();
320
321
0
  PresumedLoc PL = SM.getPresumedLoc(range.getBegin());
322
323
0
  OS << llvm::sys::path::filename(PL.getFilename());
324
0
  OS << " [" << PL.getLine() << ":"
325
0
             << PL.getColumn();
326
0
  OS << " - ";
327
328
0
  SourceLocation end = range.getEnd();
329
0
  PL = SM.getPresumedLoc(end);
330
331
0
  unsigned endCol = PL.getColumn() - 1;
332
0
  if (!range.isTokenRange())
333
0
    endCol += Lexer::MeasureTokenLength(end, SM, langOpts);
334
0
  OS << PL.getLine() << ":" << endCol << "]";
335
0
}
336
337
//===----------------------------------------------------------------------===//
338
// Command line processing.
339
//===----------------------------------------------------------------------===//
340
341
56
int main(int argc, const char **argv) {
342
56
  void *MainAddr = (void*) (intptr_t) GetExecutablePath;
343
56
  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
344
345
56
  std::string
346
56
    resourcesPath = CompilerInvocation::GetResourcesPath(argv[0], MainAddr);
347
348
56
  int optargc = 0;
349
181
  for (; optargc != argc; 
++optargc125
) {
350
151
    if (StringRef(argv[optargc]) == "--args")
351
26
      break;
352
151
  }
353
56
  llvm::cl::ParseCommandLineOptions(optargc, argv, "arcmt-test");
354
355
56
  if (VerifyTransformedFiles) {
356
30
    if (ResultFiles.empty()) {
357
0
      llvm::cl::PrintHelpMessage();
358
0
      return 1;
359
0
    }
360
30
    return verifyTransformedFiles(ResultFiles);
361
30
  }
362
363
26
  if (optargc == argc) {
364
0
    llvm::cl::PrintHelpMessage();
365
0
    return 1;
366
0
  }
367
368
26
  ArrayRef<const char*> Args(argv+optargc+1, argc-optargc-1);
369
370
26
  if (CheckOnly)
371
0
    return checkForMigration(resourcesPath, Args);
372
373
26
  return performTransformations(resourcesPath, Args);
374
26
}