Coverage Report

Created: 2020-09-22 08:39

/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
25
  PreprocessorOptions PPOpts;
143
25
  remapper.applyMappings(PPOpts);
144
  // The changed files will be in memory buffers, print them.
145
25
  for (const auto &RB : PPOpts.RemappedFileBuffers)
146
25
    OS << RB.second->getBuffer();
147
25
}
148
149
static bool performTransformations(StringRef resourcesPath,
150
26
                                   ArrayRef<const char *> Args) {
151
  // Check first.
152
26
  if (checkForMigration(resourcesPath, Args))
153
1
    return true;
154
155
25
  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
156
25
  DiagnosticConsumer *DiagClient =
157
25
    new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
158
25
  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
159
25
  IntrusiveRefCntPtr<DiagnosticsEngine> TopDiags(
160
25
      new DiagnosticsEngine(DiagID, &*DiagOpts, &*DiagClient));
161
162
25
  CompilerInvocation origCI;
163
25
  if (!CompilerInvocation::CreateFromArgs(origCI, Args, *TopDiags))
164
0
    return true;
165
166
25
  if (origCI.getFrontendOpts().Inputs.empty()) {
167
0
    llvm::errs() << "error: no input files\n";
168
0
    return true;
169
0
  }
170
171
25
  if (!origCI.getLangOpts()->ObjC)
172
0
    return false;
173
174
25
  MigrationProcess migration(origCI, std::make_shared<PCHContainerOperations>(),
175
25
                             DiagClient);
176
177
25
  std::vector<TransformFn>
178
25
    transforms = arcmt::getAllTransformations(origCI.getLangOpts()->getGC(),
179
25
                                 origCI.getMigratorOpts().NoFinalizeRemoval);
180
25
  assert(!transforms.empty());
181
182
25
  std::unique_ptr<PrintTransforms> transformPrinter;
183
25
  if (OutputTransformations)
184
0
    transformPrinter.reset(new PrintTransforms(llvm::outs()));
185
186
77
  for (unsigned i=0, e = transforms.size(); i != e; 
++i52
) {
187
52
    bool err = migration.applyTransform(transforms[i], transformPrinter.get());
188
52
    if (err) 
return true0
;
189
190
52
    if (VerboseOpt) {
191
0
      if (i == e-1)
192
0
        llvm::errs() << "\n##### FINAL RESULT #####\n";
193
0
      else
194
0
        llvm::errs() << "\n##### OUTPUT AFTER "<< i+1 <<". TRANSFORMATION #####\n";
195
0
      printResult(migration.getRemapper(), llvm::errs());
196
0
      llvm::errs() << "\n##########################\n\n";
197
0
    }
198
52
  }
199
200
25
  if (!OutputTransformations)
201
25
    printResult(migration.getRemapper(), llvm::outs());
202
203
  // FIXME: TestResultForARC
204
205
25
  return false;
206
25
}
207
208
39
static bool filesCompareEqual(StringRef fname1, StringRef fname2) {
209
39
  using namespace llvm;
210
211
39
  ErrorOr<std::unique_ptr<MemoryBuffer>> file1 = MemoryBuffer::getFile(fname1);
212
39
  if (!file1)
213
0
    return false;
214
215
39
  ErrorOr<std::unique_ptr<MemoryBuffer>> file2 = MemoryBuffer::getFile(fname2);
216
39
  if (!file2)
217
0
    return false;
218
219
39
  return file1.get()->getBuffer() == file2.get()->getBuffer();
220
39
}
221
222
30
static bool verifyTransformedFiles(ArrayRef<std::string> resultFiles) {
223
30
  using namespace llvm;
224
225
30
  assert(!resultFiles.empty());
226
227
30
  std::map<StringRef, StringRef> resultMap;
228
229
30
  for (ArrayRef<std::string>::iterator
230
69
         I = resultFiles.begin(), E = resultFiles.end(); I != E; 
++I39
) {
231
39
    StringRef fname(*I);
232
39
    if (!fname.endswith(".result")) {
233
0
      errs() << "error: filename '" << fname
234
0
                   << "' does not have '.result' extension\n";
235
0
      return true;
236
0
    }
237
39
    resultMap[sys::path::stem(fname)] = fname;
238
39
  }
239
240
30
  ErrorOr<std::unique_ptr<MemoryBuffer>> inputBuf = std::error_code();
241
30
  if (RemappingsFile.empty())
242
30
    inputBuf = MemoryBuffer::getSTDIN();
243
0
  else
244
0
    inputBuf = MemoryBuffer::getFile(RemappingsFile);
245
30
  if (!inputBuf) {
246
0
    errs() << "error: could not read remappings input\n";
247
0
    return true;
248
0
  }
249
250
30
  SmallVector<StringRef, 8> strs;
251
30
  inputBuf.get()->getBuffer().split(strs, "\n", /*MaxSplit=*/-1,
252
30
                                    /*KeepEmpty=*/false);
253
254
30
  if (strs.empty()) {
255
0
    errs() << "error: no files to verify from stdin\n";
256
0
    return true;
257
0
  }
258
30
  if (strs.size() % 2 != 0) {
259
0
    errs() << "error: files to verify are not original/result pairs\n";
260
0
    return true;
261
0
  }
262
263
69
  
for (unsigned i = 0, e = strs.size(); 30
i != e;
i += 239
) {
264
39
    StringRef inputOrigFname = strs[i];
265
39
    StringRef inputResultFname = strs[i+1];
266
267
39
    std::map<StringRef, StringRef>::iterator It;
268
39
    It = resultMap.find(sys::path::filename(inputOrigFname));
269
39
    if (It == resultMap.end()) {
270
0
      errs() << "error: '" << inputOrigFname << "' is not in the list of "
271
0
             << "transformed files to verify\n";
272
0
      return true;
273
0
    }
274
275
39
    if (!sys::fs::exists(It->second)) {
276
0
      errs() << "error: '" << It->second << "' does not exist\n";
277
0
      return true;
278
0
    }
279
39
    if (!sys::fs::exists(inputResultFname)) {
280
0
      errs() << "error: '" << inputResultFname << "' does not exist\n";
281
0
      return true;
282
0
    }
283
284
39
    if (!filesCompareEqual(It->second, inputResultFname)) {
285
0
      errs() << "error: '" << It->second << "' is different than "
286
0
             << "'" << inputResultFname << "'\n";
287
0
      return true;
288
0
    }
289
290
39
    resultMap.erase(It);
291
39
  }
292
293
30
  if (!resultMap.empty()) {
294
0
    for (std::map<StringRef, StringRef>::iterator
295
0
           I = resultMap.begin(), E = resultMap.end(); I != E; ++I)
296
0
      errs() << "error: '" << I->second << "' was not verified!\n";
297
0
    return true;
298
0
  }
299
300
30
  return false; 
301
30
}
302
303
//===----------------------------------------------------------------------===//
304
// Misc. functions.
305
//===----------------------------------------------------------------------===//
306
307
static void printSourceLocation(SourceLocation loc, ASTContext &Ctx,
308
0
                                raw_ostream &OS) {
309
0
  SourceManager &SM = Ctx.getSourceManager();
310
0
  PresumedLoc PL = SM.getPresumedLoc(loc);
311
312
0
  OS << llvm::sys::path::filename(PL.getFilename());
313
0
  OS << ":" << PL.getLine() << ":"
314
0
            << PL.getColumn();
315
0
}
316
317
static void printSourceRange(CharSourceRange range, ASTContext &Ctx,
318
0
                             raw_ostream &OS) {
319
0
  SourceManager &SM = Ctx.getSourceManager();
320
0
  const LangOptions &langOpts = Ctx.getLangOpts();
321
322
0
  PresumedLoc PL = SM.getPresumedLoc(range.getBegin());
323
324
0
  OS << llvm::sys::path::filename(PL.getFilename());
325
0
  OS << " [" << PL.getLine() << ":"
326
0
             << PL.getColumn();
327
0
  OS << " - ";
328
329
0
  SourceLocation end = range.getEnd();
330
0
  PL = SM.getPresumedLoc(end);
331
332
0
  unsigned endCol = PL.getColumn() - 1;
333
0
  if (!range.isTokenRange())
334
0
    endCol += Lexer::MeasureTokenLength(end, SM, langOpts);
335
0
  OS << PL.getLine() << ":" << endCol << "]";
336
0
}
337
338
//===----------------------------------------------------------------------===//
339
// Command line processing.
340
//===----------------------------------------------------------------------===//
341
342
56
int main(int argc, const char **argv) {
343
56
  void *MainAddr = (void*) (intptr_t) GetExecutablePath;
344
56
  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
345
346
56
  std::string
347
56
    resourcesPath = CompilerInvocation::GetResourcesPath(argv[0], MainAddr);
348
349
56
  int optargc = 0;
350
181
  for (; optargc != argc; 
++optargc125
) {
351
151
    if (StringRef(argv[optargc]) == "--args")
352
26
      break;
353
151
  }
354
56
  llvm::cl::ParseCommandLineOptions(optargc, argv, "arcmt-test");
355
356
56
  if (VerifyTransformedFiles) {
357
30
    if (ResultFiles.empty()) {
358
0
      llvm::cl::PrintHelpMessage();
359
0
      return 1;
360
0
    }
361
30
    return verifyTransformedFiles(ResultFiles);
362
30
  }
363
364
26
  if (optargc == argc) {
365
0
    llvm::cl::PrintHelpMessage();
366
0
    return 1;
367
0
  }
368
369
26
  ArrayRef<const char*> Args(argv+optargc+1, argc-optargc-1);
370
371
26
  if (CheckOnly)
372
0
    return checkForMigration(resourcesPath, Args);
373
374
26
  return performTransformations(resourcesPath, Args);
375
26
}