arcmt-test.cpp revision 1.1.1.2
154359Sroberto//===-- arcmt-test.cpp - ARC Migration Tool testbed -----------------------===// 254359Sroberto// 354359Sroberto// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 454359Sroberto// See https://llvm.org/LICENSE.txt for license information. 554359Sroberto// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 654359Sroberto// 754359Sroberto//===----------------------------------------------------------------------===// 854359Sroberto 954359Sroberto#include "clang/ARCMigrate/ARCMT.h" 1054359Sroberto#include "clang/AST/ASTContext.h" 1154359Sroberto#include "clang/Frontend/PCHContainerOperations.h" 12285612Sdelphij#include "clang/Frontend/TextDiagnosticPrinter.h" 1354359Sroberto#include "clang/Frontend/Utils.h" 1454359Sroberto#include "clang/Frontend/VerifyDiagnosticConsumer.h" 1554359Sroberto#include "clang/Lex/Preprocessor.h" 1654359Sroberto#include "clang/Lex/PreprocessorOptions.h" 1754359Sroberto#include "llvm/Support/FileSystem.h" 1854359Sroberto#include "llvm/Support/MemoryBuffer.h" 19285612Sdelphij#include "llvm/Support/Path.h" 20285612Sdelphij#include "llvm/Support/Signals.h" 2182498Sroberto#include <system_error> 2254359Sroberto 2354359Srobertousing namespace clang; 24132451Srobertousing namespace arcmt; 2554359Sroberto 2654359Srobertostatic llvm::cl::opt<bool> 2754359SrobertoCheckOnly("check-only", 2854359Sroberto llvm::cl::desc("Just check for issues that need to be handled manually")); 29285612Sdelphij 30285612Sdelphij//static llvm::cl::opt<bool> 31285612Sdelphij//TestResultForARC("test-result", 32285612Sdelphij//llvm::cl::desc("Test the result of transformations by parsing it in ARC mode")); 33285612Sdelphij 34285612Sdelphijstatic llvm::cl::opt<bool> 35285612SdelphijOutputTransformations("output-transformations", 3654359Sroberto llvm::cl::desc("Print the source transformations")); 37285612Sdelphij 38285612Sdelphijstatic llvm::cl::opt<bool> 39285612SdelphijVerifyDiags("verify",llvm::cl::desc("Verify emitted diagnostics and warnings")); 4054359Sroberto 41289999Sglebiusstatic llvm::cl::opt<bool> 4254359SrobertoVerboseOpt("v", llvm::cl::desc("Enable verbose output")); 43132451Sroberto 44285612Sdelphijstatic llvm::cl::opt<bool> 45285612SdelphijVerifyTransformedFiles("verify-transformed-files", 46285612Sdelphijllvm::cl::desc("Read pairs of file mappings (typically the output of " 47132451Sroberto "c-arcmt-test) and compare their contents with the filenames " 48285612Sdelphij "provided in command-line")); 49132451Sroberto 50285612Sdelphijstatic llvm::cl::opt<std::string> 51285612SdelphijRemappingsFile("remappings-file", 52285612Sdelphij llvm::cl::desc("Pairs of file mappings (typically the output of " 53285612Sdelphij "c-arcmt-test)")); 54285612Sdelphij 55285612Sdelphijstatic llvm::cl::list<std::string> 5654359SrobertoResultFiles(llvm::cl::Positional, llvm::cl::desc("<filename>...")); 57285612Sdelphij 58285612Sdelphijstatic llvm::cl::extrahelp extraHelp( 59285612Sdelphij "\nusage with compiler args: arcmt-test [options] --args [compiler flags]\n"); 60285612Sdelphij 61285612Sdelphij// This function isn't referenced outside its translation unit, but it 62132451Sroberto// can't use the "static" keyword because its address is used for 6354359Sroberto// GetMainExecutable (since some platforms don't support taking the 6454359Sroberto// address of main, and some platforms can't implement GetMainExecutable 65285612Sdelphij// without being given the address of a function in the main executable). 6654359Srobertostd::string GetExecutablePath(const char *Argv0) { 6754359Sroberto // This just needs to be some symbol in the binary; C++ doesn't 6854359Sroberto // allow taking the address of ::main however. 6954359Sroberto void *MainAddr = (void*) (intptr_t) GetExecutablePath; 7054359Sroberto return llvm::sys::fs::getMainExecutable(Argv0, MainAddr); 7154359Sroberto} 72285612Sdelphij 73285612Sdelphijstatic void printSourceLocation(SourceLocation loc, ASTContext &Ctx, 7454359Sroberto raw_ostream &OS); 7554359Srobertostatic void printSourceRange(CharSourceRange range, ASTContext &Ctx, 7654359Sroberto raw_ostream &OS); 77285612Sdelphij 78285612Sdelphijnamespace { 79285612Sdelphij 80132451Srobertoclass PrintTransforms : public MigrationProcess::RewriteListener { 81285612Sdelphij ASTContext *Ctx; 82285612Sdelphij raw_ostream &OS; 83285612Sdelphij 8454359Srobertopublic: 8554359Sroberto PrintTransforms(raw_ostream &OS) 8654359Sroberto : Ctx(nullptr), OS(OS) {} 87132451Sroberto 88285612Sdelphij void start(ASTContext &ctx) override { Ctx = &ctx; } 8954359Sroberto void finish() override { Ctx = nullptr; } 90285612Sdelphij 91132451Sroberto void insert(SourceLocation loc, StringRef text) override { 92285612Sdelphij assert(Ctx); 93285612Sdelphij OS << "Insert: "; 94285612Sdelphij printSourceLocation(loc, *Ctx, OS); 95285612Sdelphij OS << " \"" << text << "\"\n"; 96132451Sroberto } 97285612Sdelphij 98285612Sdelphij void remove(CharSourceRange range) override { 99285612Sdelphij assert(Ctx); 100285612Sdelphij OS << "Remove: "; 101285612Sdelphij printSourceRange(range, *Ctx, OS); 102285612Sdelphij OS << '\n'; 103285612Sdelphij } 104285612Sdelphij}; 105285612Sdelphij 106285612Sdelphij} // anonymous namespace 107132451Sroberto 108285612Sdelphijstatic bool checkForMigration(StringRef resourcesPath, 109285612Sdelphij ArrayRef<const char *> Args) { 110285612Sdelphij IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 111285612Sdelphij DiagnosticConsumer *DiagClient = 112285612Sdelphij new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); 113285612Sdelphij IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 114285612Sdelphij IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 115285612Sdelphij new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient)); 116285612Sdelphij // Chain in -verify checker, if requested. 117285612Sdelphij VerifyDiagnosticConsumer *verifyDiag = nullptr; 118285612Sdelphij if (VerifyDiags) { 119285612Sdelphij verifyDiag = new VerifyDiagnosticConsumer(*Diags); 120285612Sdelphij Diags->setClient(verifyDiag); 121285612Sdelphij } 122285612Sdelphij 123285612Sdelphij CompilerInvocation CI; 124285612Sdelphij if (!CompilerInvocation::CreateFromArgs(CI, Args, *Diags)) 125285612Sdelphij return true; 126285612Sdelphij 127285612Sdelphij if (CI.getFrontendOpts().Inputs.empty()) { 128285612Sdelphij llvm::errs() << "error: no input files\n"; 129285612Sdelphij return true; 130285612Sdelphij } 131285612Sdelphij 132285612Sdelphij if (!CI.getLangOpts()->ObjC) 133285612Sdelphij return false; 134285612Sdelphij 135285612Sdelphij arcmt::checkForManualIssues(CI, CI.getFrontendOpts().Inputs[0], 136285612Sdelphij std::make_shared<PCHContainerOperations>(), 137285612Sdelphij Diags->getClient()); 138285612Sdelphij return Diags->getClient()->getNumErrors() > 0; 139285612Sdelphij} 140285612Sdelphij 141285612Sdelphijstatic void printResult(FileRemapper &remapper, raw_ostream &OS) { 142285612Sdelphij remapper.forEachMapping([](StringRef, StringRef) {}, 143285612Sdelphij [&](StringRef, const llvm::MemoryBufferRef &Buffer) { 144285612Sdelphij OS << Buffer.getBuffer(); 145285612Sdelphij }); 146285612Sdelphij} 147132451Sroberto 148289999Sglebiusstatic bool performTransformations(StringRef resourcesPath, 149289999Sglebius ArrayRef<const char *> Args) { 150285612Sdelphij // Check first. 151285612Sdelphij if (checkForMigration(resourcesPath, Args)) 152285612Sdelphij return true; 153285612Sdelphij 154285612Sdelphij IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 155285612Sdelphij DiagnosticConsumer *DiagClient = 156285612Sdelphij new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); 157285612Sdelphij IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 158285612Sdelphij IntrusiveRefCntPtr<DiagnosticsEngine> TopDiags( 159285612Sdelphij new DiagnosticsEngine(DiagID, &*DiagOpts, &*DiagClient)); 160285612Sdelphij 161285612Sdelphij CompilerInvocation origCI; 162285612Sdelphij if (!CompilerInvocation::CreateFromArgs(origCI, Args, *TopDiags)) 163285612Sdelphij return true; 164285612Sdelphij 165285612Sdelphij if (origCI.getFrontendOpts().Inputs.empty()) { 166285612Sdelphij llvm::errs() << "error: no input files\n"; 167285612Sdelphij return true; 168285612Sdelphij } 169285612Sdelphij 170285612Sdelphij if (!origCI.getLangOpts()->ObjC) 171285612Sdelphij return false; 172285612Sdelphij 173285612Sdelphij MigrationProcess migration(origCI, std::make_shared<PCHContainerOperations>(), 174285612Sdelphij DiagClient); 175285612Sdelphij 176285612Sdelphij std::vector<TransformFn> 177285612Sdelphij transforms = arcmt::getAllTransformations(origCI.getLangOpts()->getGC(), 178285612Sdelphij origCI.getMigratorOpts().NoFinalizeRemoval); 179285612Sdelphij assert(!transforms.empty()); 180285612Sdelphij 181285612Sdelphij std::unique_ptr<PrintTransforms> transformPrinter; 182285612Sdelphij if (OutputTransformations) 183285612Sdelphij transformPrinter.reset(new PrintTransforms(llvm::outs())); 184285612Sdelphij 185285612Sdelphij for (unsigned i=0, e = transforms.size(); i != e; ++i) { 186285612Sdelphij bool err = migration.applyTransform(transforms[i], transformPrinter.get()); 187285612Sdelphij if (err) return true; 188285612Sdelphij 189285612Sdelphij if (VerboseOpt) { 190285612Sdelphij if (i == e-1) 191285612Sdelphij llvm::errs() << "\n##### FINAL RESULT #####\n"; 192285612Sdelphij else 193285612Sdelphij llvm::errs() << "\n##### OUTPUT AFTER "<< i+1 <<". TRANSFORMATION #####\n"; 194285612Sdelphij printResult(migration.getRemapper(), llvm::errs()); 195285612Sdelphij llvm::errs() << "\n##########################\n\n"; 196285612Sdelphij } 197285612Sdelphij } 198285612Sdelphij 199285612Sdelphij if (!OutputTransformations) 200285612Sdelphij printResult(migration.getRemapper(), llvm::outs()); 201285612Sdelphij 202285612Sdelphij // FIXME: TestResultForARC 203285612Sdelphij 204285612Sdelphij return false; 205285612Sdelphij} 206285612Sdelphij 207285612Sdelphijstatic bool filesCompareEqual(StringRef fname1, StringRef fname2) { 208285612Sdelphij using namespace llvm; 209285612Sdelphij 210285612Sdelphij ErrorOr<std::unique_ptr<MemoryBuffer>> file1 = 211285612Sdelphij MemoryBuffer::getFile(fname1, /*IsText=*/true); 212285612Sdelphij if (!file1) 213285612Sdelphij return false; 214285612Sdelphij 215285612Sdelphij ErrorOr<std::unique_ptr<MemoryBuffer>> file2 = 216285612Sdelphij MemoryBuffer::getFile(fname2, /*IsText=*/true); 217285612Sdelphij if (!file2) 218285612Sdelphij return false; 219285612Sdelphij 220285612Sdelphij return file1.get()->getBuffer() == file2.get()->getBuffer(); 221285612Sdelphij} 222285612Sdelphij 223285612Sdelphijstatic bool verifyTransformedFiles(ArrayRef<std::string> resultFiles) { 224285612Sdelphij using namespace llvm; 225285612Sdelphij 226285612Sdelphij assert(!resultFiles.empty()); 227285612Sdelphij 228285612Sdelphij std::map<StringRef, StringRef> resultMap; 229285612Sdelphij 230285612Sdelphij for (ArrayRef<std::string>::iterator 231285612Sdelphij I = resultFiles.begin(), E = resultFiles.end(); I != E; ++I) { 232285612Sdelphij StringRef fname(*I); 233285612Sdelphij if (!fname.endswith(".result")) { 234285612Sdelphij errs() << "error: filename '" << fname 235132451Sroberto << "' does not have '.result' extension\n"; 236285612Sdelphij return true; 237132451Sroberto } 238285612Sdelphij resultMap[sys::path::stem(fname)] = fname; 239132451Sroberto } 240132451Sroberto 241132451Sroberto ErrorOr<std::unique_ptr<MemoryBuffer>> inputBuf = std::error_code(); 242132451Sroberto if (RemappingsFile.empty()) 243132451Sroberto inputBuf = MemoryBuffer::getSTDIN(); 244285612Sdelphij else 245285612Sdelphij inputBuf = MemoryBuffer::getFile(RemappingsFile, /*IsText=*/true); 246285612Sdelphij if (!inputBuf) { 247285612Sdelphij errs() << "error: could not read remappings input\n"; 248285612Sdelphij return true; 249285612Sdelphij } 250285612Sdelphij 251132451Sroberto SmallVector<StringRef, 8> strs; 252285612Sdelphij inputBuf.get()->getBuffer().split(strs, "\n", /*MaxSplit=*/-1, 253132451Sroberto /*KeepEmpty=*/false); 254285612Sdelphij 255285612Sdelphij if (strs.empty()) { 256132451Sroberto errs() << "error: no files to verify from stdin\n"; 257132451Sroberto return true; 258285612Sdelphij } 259285612Sdelphij if (strs.size() % 2 != 0) { 260285612Sdelphij errs() << "error: files to verify are not original/result pairs\n"; 261285612Sdelphij return true; 262285612Sdelphij } 263285612Sdelphij 264132451Sroberto for (unsigned i = 0, e = strs.size(); i != e; i += 2) { 265285612Sdelphij StringRef inputOrigFname = strs[i]; 266285612Sdelphij StringRef inputResultFname = strs[i+1]; 26754359Sroberto 268285612Sdelphij std::map<StringRef, StringRef>::iterator It; 269132451Sroberto It = resultMap.find(sys::path::filename(inputOrigFname)); 270285612Sdelphij if (It == resultMap.end()) { 27154359Sroberto errs() << "error: '" << inputOrigFname << "' is not in the list of " 272132451Sroberto << "transformed files to verify\n"; 273132451Sroberto return true; 274132451Sroberto } 275132451Sroberto 276285612Sdelphij if (!sys::fs::exists(It->second)) { 277285612Sdelphij errs() << "error: '" << It->second << "' does not exist\n"; 278285612Sdelphij return true; 279285612Sdelphij } 280285612Sdelphij if (!sys::fs::exists(inputResultFname)) { 281285612Sdelphij errs() << "error: '" << inputResultFname << "' does not exist\n"; 282285612Sdelphij return true; 283132451Sroberto } 284285612Sdelphij 285132451Sroberto if (!filesCompareEqual(It->second, inputResultFname)) { 286285612Sdelphij errs() << "error: '" << It->second << "' is different than " 287285612Sdelphij << "'" << inputResultFname << "'\n"; 288132451Sroberto return true; 289285612Sdelphij } 290285612Sdelphij 291285612Sdelphij resultMap.erase(It); 292285612Sdelphij } 293285612Sdelphij 294285612Sdelphij if (!resultMap.empty()) { 295285612Sdelphij for (std::map<StringRef, StringRef>::iterator 296285612Sdelphij I = resultMap.begin(), E = resultMap.end(); I != E; ++I) 297285612Sdelphij errs() << "error: '" << I->second << "' was not verified!\n"; 298285612Sdelphij return true; 299285612Sdelphij } 300132451Sroberto 301285612Sdelphij return false; 302285612Sdelphij} 303132451Sroberto 304285612Sdelphij//===----------------------------------------------------------------------===// 305285612Sdelphij// Misc. functions. 306132451Sroberto//===----------------------------------------------------------------------===// 307285612Sdelphij 308285612Sdelphijstatic void printSourceLocation(SourceLocation loc, ASTContext &Ctx, 309285612Sdelphij raw_ostream &OS) { 310285612Sdelphij SourceManager &SM = Ctx.getSourceManager(); 311285612Sdelphij PresumedLoc PL = SM.getPresumedLoc(loc); 312285612Sdelphij 313285612Sdelphij OS << llvm::sys::path::filename(PL.getFilename()); 314285612Sdelphij OS << ":" << PL.getLine() << ":" 315285612Sdelphij << PL.getColumn(); 316285612Sdelphij} 317285612Sdelphij 318285612Sdelphijstatic void printSourceRange(CharSourceRange range, ASTContext &Ctx, 31954359Sroberto raw_ostream &OS) { 320285612Sdelphij SourceManager &SM = Ctx.getSourceManager(); 321285612Sdelphij const LangOptions &langOpts = Ctx.getLangOpts(); 32254359Sroberto 323285612Sdelphij PresumedLoc PL = SM.getPresumedLoc(range.getBegin()); 324285612Sdelphij 325285612Sdelphij OS << llvm::sys::path::filename(PL.getFilename()); 326285612Sdelphij OS << " [" << PL.getLine() << ":" 327285612Sdelphij << PL.getColumn(); 328285612Sdelphij OS << " - "; 329285612Sdelphij 330285612Sdelphij SourceLocation end = range.getEnd(); 331285612Sdelphij PL = SM.getPresumedLoc(end); 332285612Sdelphij 333285612Sdelphij unsigned endCol = PL.getColumn() - 1; 334293893Sglebius if (!range.isTokenRange()) 335285612Sdelphij endCol += Lexer::MeasureTokenLength(end, SM, langOpts); 336285612Sdelphij OS << PL.getLine() << ":" << endCol << "]"; 337285612Sdelphij} 338285612Sdelphij 339285612Sdelphij//===----------------------------------------------------------------------===// 340285612Sdelphij// Command line processing. 341285612Sdelphij//===----------------------------------------------------------------------===// 342285612Sdelphij 343285612Sdelphijint main(int argc, const char **argv) { 344285612Sdelphij void *MainAddr = (void*) (intptr_t) GetExecutablePath; 345285612Sdelphij llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); 346285612Sdelphij 347285612Sdelphij std::string 348285612Sdelphij resourcesPath = CompilerInvocation::GetResourcesPath(argv[0], MainAddr); 349285612Sdelphij 350285612Sdelphij int optargc = 0; 351285612Sdelphij for (; optargc != argc; ++optargc) { 352285612Sdelphij if (StringRef(argv[optargc]) == "--args") 353285612Sdelphij break; 354285612Sdelphij } 355285612Sdelphij llvm::cl::ParseCommandLineOptions(optargc, argv, "arcmt-test"); 356285612Sdelphij 357285612Sdelphij if (VerifyTransformedFiles) { 358285612Sdelphij if (ResultFiles.empty()) { 359285612Sdelphij llvm::cl::PrintHelpMessage(); 360289999Sglebius return 1; 361289999Sglebius } 362289999Sglebius return verifyTransformedFiles(ResultFiles); 363289999Sglebius } 364289999Sglebius 365289999Sglebius if (optargc == argc) { 366289999Sglebius llvm::cl::PrintHelpMessage(); 367289999Sglebius return 1; 368289999Sglebius } 369289999Sglebius 370289999Sglebius ArrayRef<const char*> Args(argv+optargc+1, argc-optargc-1); 371285612Sdelphij 372289999Sglebius if (CheckOnly) 373285612Sdelphij return checkForMigration(resourcesPath, Args); 374285612Sdelphij 375285612Sdelphij return performTransformations(resourcesPath, Args); 376285612Sdelphij} 377285612Sdelphij