blob: f291dec21fda0cd923af3173c51db9cf321770af [file] [log] [blame]
John McCalld70fb982011-06-15 23:25:17 +00001//===--- ARCMT.cpp - Migration to ARC mode --------------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "Internals.h"
11#include "clang/Frontend/ASTUnit.h"
12#include "clang/Frontend/CompilerInstance.h"
Douglas Gregor32fbe312012-01-20 16:28:04 +000013#include "clang/Frontend/FrontendAction.h"
Argyrios Kyrtzidisd5713632011-07-19 17:20:03 +000014#include "clang/Frontend/TextDiagnosticPrinter.h"
John McCalld70fb982011-06-15 23:25:17 +000015#include "clang/Frontend/Utils.h"
16#include "clang/AST/ASTConsumer.h"
17#include "clang/Rewrite/Rewriter.h"
18#include "clang/Sema/SemaDiagnostic.h"
19#include "clang/Basic/DiagnosticCategories.h"
20#include "clang/Lex/Preprocessor.h"
21#include "llvm/Support/MemoryBuffer.h"
22#include "llvm/ADT/Triple.h"
John McCalld70fb982011-06-15 23:25:17 +000023using namespace clang;
24using namespace arcmt;
John McCalld70fb982011-06-15 23:25:17 +000025
Chris Lattner54b16772011-07-23 17:14:25 +000026bool CapturedDiagList::clearDiagnostic(ArrayRef<unsigned> IDs,
John McCalld70fb982011-06-15 23:25:17 +000027 SourceRange range) {
28 if (range.isInvalid())
29 return false;
30
31 bool cleared = false;
32 ListTy::iterator I = List.begin();
33 while (I != List.end()) {
34 FullSourceLoc diagLoc = I->getLocation();
35 if ((IDs.empty() || // empty means clear all diagnostics in the range.
36 std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) &&
37 !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
38 (diagLoc == range.getEnd() ||
39 diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
40 cleared = true;
41 ListTy::iterator eraseS = I++;
David Blaikie9c902b52011-09-25 23:23:43 +000042 while (I != List.end() && I->getLevel() == DiagnosticsEngine::Note)
John McCalld70fb982011-06-15 23:25:17 +000043 ++I;
44 // Clear the diagnostic and any notes following it.
45 List.erase(eraseS, I);
46 continue;
47 }
48
49 ++I;
50 }
51
52 return cleared;
53}
54
Chris Lattner54b16772011-07-23 17:14:25 +000055bool CapturedDiagList::hasDiagnostic(ArrayRef<unsigned> IDs,
Argyrios Kyrtzidis0f3f9f72011-06-18 00:53:34 +000056 SourceRange range) const {
John McCalld70fb982011-06-15 23:25:17 +000057 if (range.isInvalid())
58 return false;
59
Argyrios Kyrtzidis0f3f9f72011-06-18 00:53:34 +000060 ListTy::const_iterator I = List.begin();
John McCalld70fb982011-06-15 23:25:17 +000061 while (I != List.end()) {
62 FullSourceLoc diagLoc = I->getLocation();
63 if ((IDs.empty() || // empty means any diagnostic in the range.
64 std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) &&
65 !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
66 (diagLoc == range.getEnd() ||
67 diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
68 return true;
69 }
70
71 ++I;
72 }
73
74 return false;
75}
76
David Blaikie9c902b52011-09-25 23:23:43 +000077void CapturedDiagList::reportDiagnostics(DiagnosticsEngine &Diags) const {
Argyrios Kyrtzidis0f3f9f72011-06-18 00:53:34 +000078 for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
John McCalld70fb982011-06-15 23:25:17 +000079 Diags.Report(*I);
80}
81
Argyrios Kyrtzidis90b6a2a2011-06-18 00:53:41 +000082bool CapturedDiagList::hasErrors() const {
83 for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
David Blaikie9c902b52011-09-25 23:23:43 +000084 if (I->getLevel() >= DiagnosticsEngine::Error)
Argyrios Kyrtzidis90b6a2a2011-06-18 00:53:41 +000085 return true;
86
87 return false;
88}
89
John McCalld70fb982011-06-15 23:25:17 +000090namespace {
91
David Blaikiea24a0bc2011-09-25 23:54:33 +000092class CaptureDiagnosticConsumer : public DiagnosticConsumer {
David Blaikie9c902b52011-09-25 23:23:43 +000093 DiagnosticsEngine &Diags;
Jordan Roseb00073d2012-08-10 01:06:16 +000094 DiagnosticConsumer &DiagClient;
John McCalld70fb982011-06-15 23:25:17 +000095 CapturedDiagList &CapturedDiags;
Jordan Roseb00073d2012-08-10 01:06:16 +000096 bool HasBegunSourceFile;
John McCalld70fb982011-06-15 23:25:17 +000097public:
David Blaikiea24a0bc2011-09-25 23:54:33 +000098 CaptureDiagnosticConsumer(DiagnosticsEngine &diags,
Jordan Roseb00073d2012-08-10 01:06:16 +000099 DiagnosticConsumer &client,
100 CapturedDiagList &capturedDiags)
101 : Diags(diags), DiagClient(client), CapturedDiags(capturedDiags),
102 HasBegunSourceFile(false) { }
103
104 virtual void BeginSourceFile(const LangOptions &Opts,
105 const Preprocessor *PP) {
106 // Pass BeginSourceFile message onto DiagClient on first call.
107 // The corresponding EndSourceFile call will be made from an
108 // explicit call to FinishCapture.
109 if (!HasBegunSourceFile) {
110 DiagClient.BeginSourceFile(Opts, PP);
111 HasBegunSourceFile = true;
112 }
113 }
114
115 void FinishCapture() {
116 // Call EndSourceFile on DiagClient on completion of capture to
117 // enable VerifyDiagnosticConsumer to check diagnostics *after*
118 // it has received the diagnostic list.
119 if (HasBegunSourceFile) {
120 DiagClient.EndSourceFile();
121 HasBegunSourceFile = false;
122 }
123 }
124
125 virtual ~CaptureDiagnosticConsumer() {
126 assert(!HasBegunSourceFile && "FinishCapture not called!");
127 }
John McCalld70fb982011-06-15 23:25:17 +0000128
David Blaikie9c902b52011-09-25 23:23:43 +0000129 virtual void HandleDiagnostic(DiagnosticsEngine::Level level,
David Blaikieb5784322011-09-26 01:18:08 +0000130 const Diagnostic &Info) {
Ted Kremenek337c5b82011-10-20 05:07:47 +0000131 if (DiagnosticIDs::isARCDiagnostic(Info.getID()) ||
David Blaikie9c902b52011-09-25 23:23:43 +0000132 level >= DiagnosticsEngine::Error || level == DiagnosticsEngine::Note) {
John McCalld70fb982011-06-15 23:25:17 +0000133 CapturedDiags.push_back(StoredDiagnostic(level, Info));
134 return;
135 }
136
137 // Non-ARC warnings are ignored.
138 Diags.setLastDiagnosticIgnored();
139 }
Douglas Gregord0e9e3a2011-09-29 00:38:00 +0000140
141 DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
142 // Just drop any diagnostics that come from cloned consumers; they'll
143 // have different source managers anyway.
144 return new IgnoringDiagConsumer();
145 }
John McCalld70fb982011-06-15 23:25:17 +0000146};
147
148} // end anonymous namespace
149
Argyrios Kyrtzidis697729a2011-10-18 00:22:49 +0000150static inline StringRef SimulatorVersionDefineName() {
151 return "__IPHONE_OS_VERSION_MIN_REQUIRED=";
152}
153
154/// \brief Parse the simulator version define:
155/// __IPHONE_OS_VERSION_MIN_REQUIRED=([0-9])([0-9][0-9])([0-9][0-9])
156// and return the grouped values as integers, e.g:
157// __IPHONE_OS_VERSION_MIN_REQUIRED=40201
158// will return Major=4, Minor=2, Micro=1.
159static bool GetVersionFromSimulatorDefine(StringRef define,
160 unsigned &Major, unsigned &Minor,
161 unsigned &Micro) {
162 assert(define.startswith(SimulatorVersionDefineName()));
163 StringRef name, version;
164 llvm::tie(name, version) = define.split('=');
165 if (version.empty())
166 return false;
167 std::string verstr = version.str();
168 char *end;
169 unsigned num = (unsigned) strtol(verstr.c_str(), &end, 10);
170 if (*end != '\0')
171 return false;
172 Major = num / 10000;
173 num = num % 10000;
174 Minor = num / 100;
175 Micro = num % 100;
176 return true;
177}
178
Argyrios Kyrtzidis81a35902011-06-20 19:59:52 +0000179static bool HasARCRuntime(CompilerInvocation &origCI) {
180 // This duplicates some functionality from Darwin::AddDeploymentTarget
181 // but this function is well defined, so keep it decoupled from the driver
182 // and avoid unrelated complications.
183
Argyrios Kyrtzidis697729a2011-10-18 00:22:49 +0000184 for (unsigned i = 0, e = origCI.getPreprocessorOpts().Macros.size();
185 i != e; ++i) {
186 StringRef define = origCI.getPreprocessorOpts().Macros[i].first;
187 bool isUndef = origCI.getPreprocessorOpts().Macros[i].second;
188 if (isUndef)
189 continue;
190 if (!define.startswith(SimulatorVersionDefineName()))
191 continue;
192 unsigned Major = 0, Minor = 0, Micro = 0;
193 if (GetVersionFromSimulatorDefine(define, Major, Minor, Micro) &&
194 Major < 10 && Minor < 100 && Micro < 100)
195 return Major >= 5;
196 }
197
Argyrios Kyrtzidis81a35902011-06-20 19:59:52 +0000198 llvm::Triple triple(origCI.getTargetOpts().Triple);
199
200 if (triple.getOS() == llvm::Triple::IOS)
201 return triple.getOSMajorVersion() >= 5;
202
203 if (triple.getOS() == llvm::Triple::Darwin)
204 return triple.getOSMajorVersion() >= 11;
205
206 if (triple.getOS() == llvm::Triple::MacOSX) {
207 unsigned Major, Minor, Micro;
208 triple.getOSVersion(Major, Minor, Micro);
209 return Major > 10 || (Major == 10 && Minor >= 7);
210 }
211
212 return false;
213}
214
Benjamin Kramer3c05b7c2011-08-02 04:50:49 +0000215static CompilerInvocation *
216createInvocationForMigration(CompilerInvocation &origCI) {
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000217 OwningPtr<CompilerInvocation> CInvok;
John McCalld70fb982011-06-15 23:25:17 +0000218 CInvok.reset(new CompilerInvocation(origCI));
219 CInvok->getPreprocessorOpts().ImplicitPCHInclude = std::string();
220 CInvok->getPreprocessorOpts().ImplicitPTHInclude = std::string();
221 std::string define = getARCMTMacroName();
222 define += '=';
223 CInvok->getPreprocessorOpts().addMacroDef(define);
Ted Kremenek8cf47df2011-11-17 23:01:24 +0000224 CInvok->getLangOpts()->ObjCAutoRefCount = true;
225 CInvok->getLangOpts()->setGC(LangOptions::NonGC);
John McCalld70fb982011-06-15 23:25:17 +0000226 CInvok->getDiagnosticOpts().ErrorLimit = 0;
Argyrios Kyrtzidisaa38f692012-06-20 01:46:26 +0000227 CInvok->getDiagnosticOpts().PedanticErrors = 0;
Argyrios Kyrtzidis692bf8c2012-06-20 01:10:40 +0000228
229 // Ignore -Werror flags when migrating.
230 std::vector<std::string> WarnOpts;
231 for (std::vector<std::string>::iterator
232 I = CInvok->getDiagnosticOpts().Warnings.begin(),
233 E = CInvok->getDiagnosticOpts().Warnings.end(); I != E; ++I) {
234 if (!StringRef(*I).startswith("error"))
235 WarnOpts.push_back(*I);
236 }
237 WarnOpts.push_back("error=arc-unsafe-retained-assign");
Argyrios Kyrtzidisaa38f692012-06-20 01:46:26 +0000238 CInvok->getDiagnosticOpts().Warnings = llvm_move(WarnOpts);
Argyrios Kyrtzidis692bf8c2012-06-20 01:10:40 +0000239
Ted Kremenek8cf47df2011-11-17 23:01:24 +0000240 CInvok->getLangOpts()->ObjCRuntimeHasWeak = HasARCRuntime(origCI);
John McCalld70fb982011-06-15 23:25:17 +0000241
242 return CInvok.take();
243}
244
Benjamin Kramer3c05b7c2011-08-02 04:50:49 +0000245static void emitPremigrationErrors(const CapturedDiagList &arcDiags,
246 const DiagnosticOptions &diagOpts,
247 Preprocessor &PP) {
Argyrios Kyrtzidisd5713632011-07-19 17:20:03 +0000248 TextDiagnosticPrinter printer(llvm::errs(), diagOpts);
Dylan Noblesmithc95d8192012-02-20 14:00:23 +0000249 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
250 IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
David Blaikie9c902b52011-09-25 23:23:43 +0000251 new DiagnosticsEngine(DiagID, &printer, /*ShouldOwnClient=*/false));
Argyrios Kyrtzidisd5713632011-07-19 17:20:03 +0000252 Diags->setSourceManager(&PP.getSourceManager());
253
David Blaikiebbafb8a2012-03-11 07:00:24 +0000254 printer.BeginSourceFile(PP.getLangOpts(), &PP);
Argyrios Kyrtzidisd5713632011-07-19 17:20:03 +0000255 arcDiags.reportDiagnostics(*Diags);
256 printer.EndSourceFile();
257}
258
John McCalld70fb982011-06-15 23:25:17 +0000259//===----------------------------------------------------------------------===//
260// checkForManualIssues.
261//===----------------------------------------------------------------------===//
262
263bool arcmt::checkForManualIssues(CompilerInvocation &origCI,
Douglas Gregor32fbe312012-01-20 16:28:04 +0000264 const FrontendInputFile &Input,
David Blaikiee2eefae2011-09-25 23:39:51 +0000265 DiagnosticConsumer *DiagClient,
Argyrios Kyrtzidisd5713632011-07-19 17:20:03 +0000266 bool emitPremigrationARCErrors,
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000267 StringRef plistOut) {
Ted Kremenek8cf47df2011-11-17 23:01:24 +0000268 if (!origCI.getLangOpts()->ObjC1)
John McCalld70fb982011-06-15 23:25:17 +0000269 return false;
270
Ted Kremenek8cf47df2011-11-17 23:01:24 +0000271 LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
Fariborz Jahanianaa7b9aa2012-01-25 00:20:29 +0000272 bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError;
Fariborz Jahanian0c859d62012-01-26 00:08:04 +0000273 bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
Argyrios Kyrtzidisd208ef92011-11-04 15:58:08 +0000274
Fariborz Jahanian48fd81b2012-01-26 20:57:58 +0000275 std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
276 NoFinalizeRemoval);
John McCalld70fb982011-06-15 23:25:17 +0000277 assert(!transforms.empty());
278
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000279 OwningPtr<CompilerInvocation> CInvok;
John McCalld70fb982011-06-15 23:25:17 +0000280 CInvok.reset(createInvocationForMigration(origCI));
281 CInvok->getFrontendOpts().Inputs.clear();
Douglas Gregor32fbe312012-01-20 16:28:04 +0000282 CInvok->getFrontendOpts().Inputs.push_back(Input);
John McCalld70fb982011-06-15 23:25:17 +0000283
284 CapturedDiagList capturedDiags;
285
286 assert(DiagClient);
Dylan Noblesmithc95d8192012-02-20 14:00:23 +0000287 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
288 IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
David Blaikie9c902b52011-09-25 23:23:43 +0000289 new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
John McCalld70fb982011-06-15 23:25:17 +0000290
291 // Filter of all diagnostics.
Jordan Roseb00073d2012-08-10 01:06:16 +0000292 CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
John McCalld70fb982011-06-15 23:25:17 +0000293 Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
294
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000295 OwningPtr<ASTUnit> Unit(
John McCalld70fb982011-06-15 23:25:17 +0000296 ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags));
Jordan Roseb00073d2012-08-10 01:06:16 +0000297 if (!Unit) {
298 errRec.FinishCapture();
John McCalld70fb982011-06-15 23:25:17 +0000299 return true;
Jordan Roseb00073d2012-08-10 01:06:16 +0000300 }
John McCalld70fb982011-06-15 23:25:17 +0000301
302 // Don't filter diagnostics anymore.
303 Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
304
305 ASTContext &Ctx = Unit->getASTContext();
306
307 if (Diags->hasFatalErrorOccurred()) {
308 Diags->Reset();
David Blaikiebbafb8a2012-03-11 07:00:24 +0000309 DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
John McCalld70fb982011-06-15 23:25:17 +0000310 capturedDiags.reportDiagnostics(*Diags);
311 DiagClient->EndSourceFile();
Jordan Roseb00073d2012-08-10 01:06:16 +0000312 errRec.FinishCapture();
John McCalld70fb982011-06-15 23:25:17 +0000313 return true;
314 }
315
Argyrios Kyrtzidisd5713632011-07-19 17:20:03 +0000316 if (emitPremigrationARCErrors)
317 emitPremigrationErrors(capturedDiags, origCI.getDiagnosticOpts(),
318 Unit->getPreprocessor());
319 if (!plistOut.empty()) {
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000320 SmallVector<StoredDiagnostic, 8> arcDiags;
Argyrios Kyrtzidisd5713632011-07-19 17:20:03 +0000321 for (CapturedDiagList::iterator
322 I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I)
323 arcDiags.push_back(*I);
324 writeARCDiagsToPlist(plistOut, arcDiags,
David Blaikiebbafb8a2012-03-11 07:00:24 +0000325 Ctx.getSourceManager(), Ctx.getLangOpts());
Argyrios Kyrtzidisd5713632011-07-19 17:20:03 +0000326 }
327
John McCalld70fb982011-06-15 23:25:17 +0000328 // After parsing of source files ended, we want to reuse the
329 // diagnostics objects to emit further diagnostics.
David Blaikiee2eefae2011-09-25 23:39:51 +0000330 // We call BeginSourceFile because DiagnosticConsumer requires that
John McCalld70fb982011-06-15 23:25:17 +0000331 // diagnostics with source range information are emitted only in between
332 // BeginSourceFile() and EndSourceFile().
David Blaikiebbafb8a2012-03-11 07:00:24 +0000333 DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
John McCalld70fb982011-06-15 23:25:17 +0000334
335 // No macros will be added since we are just checking and we won't modify
336 // source code.
337 std::vector<SourceLocation> ARCMTMacroLocs;
338
339 TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
Argyrios Kyrtzidisd208ef92011-11-04 15:58:08 +0000340 MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, ARCMTMacroLocs);
Fariborz Jahanianaa7b9aa2012-01-25 00:20:29 +0000341 pass.setNSAllocReallocError(NoNSAllocReallocError);
Fariborz Jahanian0c859d62012-01-26 00:08:04 +0000342 pass.setNoFinalizeRemoval(NoFinalizeRemoval);
John McCalld70fb982011-06-15 23:25:17 +0000343
344 for (unsigned i=0, e = transforms.size(); i != e; ++i)
345 transforms[i](pass);
346
347 capturedDiags.reportDiagnostics(*Diags);
348
349 DiagClient->EndSourceFile();
Jordan Roseb00073d2012-08-10 01:06:16 +0000350 errRec.FinishCapture();
John McCalld70fb982011-06-15 23:25:17 +0000351
Argyrios Kyrtzidise2e40b42011-07-14 00:17:54 +0000352 // If we are migrating code that gets the '-fobjc-arc' flag, make sure
353 // to remove it so that we don't get errors from normal compilation.
Ted Kremenek8cf47df2011-11-17 23:01:24 +0000354 origCI.getLangOpts()->ObjCAutoRefCount = false;
Argyrios Kyrtzidise2e40b42011-07-14 00:17:54 +0000355
Argyrios Kyrtzidis73a0d322011-07-18 07:44:45 +0000356 return capturedDiags.hasErrors() || testAct.hasReportedErrors();
John McCalld70fb982011-06-15 23:25:17 +0000357}
358
359//===----------------------------------------------------------------------===//
360// applyTransformations.
361//===----------------------------------------------------------------------===//
362
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000363static bool applyTransforms(CompilerInvocation &origCI,
Douglas Gregor32fbe312012-01-20 16:28:04 +0000364 const FrontendInputFile &Input,
David Blaikiee2eefae2011-09-25 23:39:51 +0000365 DiagnosticConsumer *DiagClient,
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000366 StringRef outputDir,
Argyrios Kyrtzidisd5713632011-07-19 17:20:03 +0000367 bool emitPremigrationARCErrors,
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000368 StringRef plistOut) {
Ted Kremenek8cf47df2011-11-17 23:01:24 +0000369 if (!origCI.getLangOpts()->ObjC1)
John McCalld70fb982011-06-15 23:25:17 +0000370 return false;
371
Ted Kremenek8cf47df2011-11-17 23:01:24 +0000372 LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC();
Argyrios Kyrtzidisd208ef92011-11-04 15:58:08 +0000373
John McCalld70fb982011-06-15 23:25:17 +0000374 // Make sure checking is successful first.
375 CompilerInvocation CInvokForCheck(origCI);
Douglas Gregor32fbe312012-01-20 16:28:04 +0000376 if (arcmt::checkForManualIssues(CInvokForCheck, Input, DiagClient,
Argyrios Kyrtzidisd5713632011-07-19 17:20:03 +0000377 emitPremigrationARCErrors, plistOut))
John McCalld70fb982011-06-15 23:25:17 +0000378 return true;
379
380 CompilerInvocation CInvok(origCI);
381 CInvok.getFrontendOpts().Inputs.clear();
Douglas Gregor32fbe312012-01-20 16:28:04 +0000382 CInvok.getFrontendOpts().Inputs.push_back(Input);
John McCalld70fb982011-06-15 23:25:17 +0000383
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000384 MigrationProcess migration(CInvok, DiagClient, outputDir);
Fariborz Jahanian48fd81b2012-01-26 20:57:58 +0000385 bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval;
John McCalld70fb982011-06-15 23:25:17 +0000386
Fariborz Jahanian48fd81b2012-01-26 20:57:58 +0000387 std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode,
388 NoFinalizeRemoval);
John McCalld70fb982011-06-15 23:25:17 +0000389 assert(!transforms.empty());
390
391 for (unsigned i=0, e = transforms.size(); i != e; ++i) {
392 bool err = migration.applyTransform(transforms[i]);
393 if (err) return true;
394 }
395
Dylan Noblesmithc95d8192012-02-20 14:00:23 +0000396 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
397 IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
David Blaikie9c902b52011-09-25 23:23:43 +0000398 new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000399
400 if (outputDir.empty()) {
Ted Kremenek8cf47df2011-11-17 23:01:24 +0000401 origCI.getLangOpts()->ObjCAutoRefCount = true;
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000402 return migration.getRemapper().overwriteOriginal(*Diags);
Argyrios Kyrtzidise2e40b42011-07-14 00:17:54 +0000403 } else {
404 // If we are migrating code that gets the '-fobjc-arc' flag, make sure
405 // to remove it so that we don't get errors from normal compilation.
Ted Kremenek8cf47df2011-11-17 23:01:24 +0000406 origCI.getLangOpts()->ObjCAutoRefCount = false;
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000407 return migration.getRemapper().flushToDisk(outputDir, *Diags);
Argyrios Kyrtzidise2e40b42011-07-14 00:17:54 +0000408 }
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000409}
410
411bool arcmt::applyTransformations(CompilerInvocation &origCI,
Douglas Gregor32fbe312012-01-20 16:28:04 +0000412 const FrontendInputFile &Input,
David Blaikiee2eefae2011-09-25 23:39:51 +0000413 DiagnosticConsumer *DiagClient) {
Douglas Gregor32fbe312012-01-20 16:28:04 +0000414 return applyTransforms(origCI, Input, DiagClient,
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000415 StringRef(), false, StringRef());
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000416}
417
418bool arcmt::migrateWithTemporaryFiles(CompilerInvocation &origCI,
Douglas Gregor32fbe312012-01-20 16:28:04 +0000419 const FrontendInputFile &Input,
David Blaikiee2eefae2011-09-25 23:39:51 +0000420 DiagnosticConsumer *DiagClient,
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000421 StringRef outputDir,
Argyrios Kyrtzidisd5713632011-07-19 17:20:03 +0000422 bool emitPremigrationARCErrors,
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000423 StringRef plistOut) {
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000424 assert(!outputDir.empty() && "Expected output directory path");
Douglas Gregor32fbe312012-01-20 16:28:04 +0000425 return applyTransforms(origCI, Input, DiagClient,
Argyrios Kyrtzidisd5713632011-07-19 17:20:03 +0000426 outputDir, emitPremigrationARCErrors, plistOut);
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000427}
428
429bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > &
430 remap,
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000431 StringRef outputDir,
David Blaikiee2eefae2011-09-25 23:39:51 +0000432 DiagnosticConsumer *DiagClient) {
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000433 assert(!outputDir.empty());
John McCalld70fb982011-06-15 23:25:17 +0000434
Dylan Noblesmithc95d8192012-02-20 14:00:23 +0000435 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
436 IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
David Blaikie9c902b52011-09-25 23:23:43 +0000437 new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000438
439 FileRemapper remapper;
440 bool err = remapper.initFromDisk(outputDir, *Diags,
441 /*ignoreIfFilesChanged=*/true);
442 if (err)
443 return true;
444
Ted Kremenekf7639e12012-03-06 20:06:33 +0000445 PreprocessorOptions PPOpts;
446 remapper.applyMappings(PPOpts);
447 remap = PPOpts.RemappedFiles;
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000448
449 return false;
John McCalld70fb982011-06-15 23:25:17 +0000450}
451
Ted Kremenekf7639e12012-03-06 20:06:33 +0000452bool arcmt::getFileRemappingsFromFileList(
453 std::vector<std::pair<std::string,std::string> > &remap,
454 ArrayRef<StringRef> remapFiles,
455 DiagnosticConsumer *DiagClient) {
456 bool hasErrorOccurred = false;
457 llvm::StringMap<bool> Uniquer;
458
459 llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
460 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
461 new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
462
463 for (ArrayRef<StringRef>::iterator
464 I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) {
465 StringRef file = *I;
466
467 FileRemapper remapper;
468 bool err = remapper.initFromFile(file, *Diags,
469 /*ignoreIfFilesChanged=*/true);
470 hasErrorOccurred = hasErrorOccurred || err;
471 if (err)
472 continue;
473
474 PreprocessorOptions PPOpts;
475 remapper.applyMappings(PPOpts);
476 for (PreprocessorOptions::remapped_file_iterator
477 RI = PPOpts.remapped_file_begin(), RE = PPOpts.remapped_file_end();
478 RI != RE; ++RI) {
479 bool &inserted = Uniquer[RI->first];
480 if (inserted)
481 continue;
482 inserted = true;
483 remap.push_back(*RI);
484 }
485 }
486
487 return hasErrorOccurred;
488}
489
John McCalld70fb982011-06-15 23:25:17 +0000490//===----------------------------------------------------------------------===//
John McCalld70fb982011-06-15 23:25:17 +0000491// CollectTransformActions.
492//===----------------------------------------------------------------------===//
493
494namespace {
495
496class ARCMTMacroTrackerPPCallbacks : public PPCallbacks {
497 std::vector<SourceLocation> &ARCMTMacroLocs;
498
499public:
500 ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs)
501 : ARCMTMacroLocs(ARCMTMacroLocs) { }
502
Argyrios Kyrtzidis85a14bb2011-08-18 01:05:45 +0000503 virtual void MacroExpands(const Token &MacroNameTok, const MacroInfo *MI,
504 SourceRange Range) {
John McCalld70fb982011-06-15 23:25:17 +0000505 if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName())
506 ARCMTMacroLocs.push_back(MacroNameTok.getLocation());
507 }
508};
509
510class ARCMTMacroTrackerAction : public ASTFrontendAction {
511 std::vector<SourceLocation> &ARCMTMacroLocs;
512
513public:
514 ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs)
515 : ARCMTMacroLocs(ARCMTMacroLocs) { }
516
517 virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000518 StringRef InFile) {
John McCalld70fb982011-06-15 23:25:17 +0000519 CI.getPreprocessor().addPPCallbacks(
520 new ARCMTMacroTrackerPPCallbacks(ARCMTMacroLocs));
521 return new ASTConsumer();
522 }
523};
524
525class RewritesApplicator : public TransformActions::RewriteReceiver {
526 Rewriter &rewriter;
John McCalld70fb982011-06-15 23:25:17 +0000527 MigrationProcess::RewriteListener *Listener;
528
529public:
530 RewritesApplicator(Rewriter &rewriter, ASTContext &ctx,
531 MigrationProcess::RewriteListener *listener)
Benjamin Kramerd1d76b22012-06-06 17:32:50 +0000532 : rewriter(rewriter), Listener(listener) {
John McCalld70fb982011-06-15 23:25:17 +0000533 if (Listener)
534 Listener->start(ctx);
535 }
536 ~RewritesApplicator() {
537 if (Listener)
538 Listener->finish();
539 }
540
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000541 virtual void insert(SourceLocation loc, StringRef text) {
John McCalld70fb982011-06-15 23:25:17 +0000542 bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true,
543 /*indentNewLines=*/true);
544 if (!err && Listener)
545 Listener->insert(loc, text);
546 }
547
548 virtual void remove(CharSourceRange range) {
549 Rewriter::RewriteOptions removeOpts;
550 removeOpts.IncludeInsertsAtBeginOfRange = false;
551 removeOpts.IncludeInsertsAtEndOfRange = false;
552 removeOpts.RemoveLineIfEmpty = true;
553
554 bool err = rewriter.RemoveText(range, removeOpts);
555 if (!err && Listener)
556 Listener->remove(range);
557 }
558
559 virtual void increaseIndentation(CharSourceRange range,
560 SourceLocation parentIndent) {
561 rewriter.IncreaseIndentation(range, parentIndent);
562 }
563};
564
565} // end anonymous namespace.
566
567/// \brief Anchor for VTable.
568MigrationProcess::RewriteListener::~RewriteListener() { }
569
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000570MigrationProcess::MigrationProcess(const CompilerInvocation &CI,
David Blaikiee2eefae2011-09-25 23:39:51 +0000571 DiagnosticConsumer *diagClient,
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000572 StringRef outputDir)
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000573 : OrigCI(CI), DiagClient(diagClient) {
574 if (!outputDir.empty()) {
Dylan Noblesmithc95d8192012-02-20 14:00:23 +0000575 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
576 IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
David Blaikie9c902b52011-09-25 23:23:43 +0000577 new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
Argyrios Kyrtzidis7fbd97f2011-07-09 20:00:58 +0000578 Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true);
579 }
580}
581
John McCalld70fb982011-06-15 23:25:17 +0000582bool MigrationProcess::applyTransform(TransformFn trans,
583 RewriteListener *listener) {
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000584 OwningPtr<CompilerInvocation> CInvok;
John McCalld70fb982011-06-15 23:25:17 +0000585 CInvok.reset(createInvocationForMigration(OrigCI));
586 CInvok->getDiagnosticOpts().IgnoreWarnings = true;
587
Ted Kremenekf7639e12012-03-06 20:06:33 +0000588 Remapper.applyMappings(CInvok->getPreprocessorOpts());
John McCalld70fb982011-06-15 23:25:17 +0000589
590 CapturedDiagList capturedDiags;
591 std::vector<SourceLocation> ARCMTMacroLocs;
592
593 assert(DiagClient);
Dylan Noblesmithc95d8192012-02-20 14:00:23 +0000594 IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
595 IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
David Blaikie9c902b52011-09-25 23:23:43 +0000596 new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
John McCalld70fb982011-06-15 23:25:17 +0000597
598 // Filter of all diagnostics.
Jordan Roseb00073d2012-08-10 01:06:16 +0000599 CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags);
John McCalld70fb982011-06-15 23:25:17 +0000600 Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
601
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000602 OwningPtr<ARCMTMacroTrackerAction> ASTAction;
John McCalld70fb982011-06-15 23:25:17 +0000603 ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs));
604
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000605 OwningPtr<ASTUnit> Unit(
John McCalld70fb982011-06-15 23:25:17 +0000606 ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags,
607 ASTAction.get()));
Jordan Roseb00073d2012-08-10 01:06:16 +0000608 if (!Unit) {
609 errRec.FinishCapture();
John McCalld70fb982011-06-15 23:25:17 +0000610 return true;
Jordan Roseb00073d2012-08-10 01:06:16 +0000611 }
John McCalld70fb982011-06-15 23:25:17 +0000612 Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that.
613
614 // Don't filter diagnostics anymore.
615 Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
616
617 ASTContext &Ctx = Unit->getASTContext();
618
619 if (Diags->hasFatalErrorOccurred()) {
620 Diags->Reset();
David Blaikiebbafb8a2012-03-11 07:00:24 +0000621 DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
John McCalld70fb982011-06-15 23:25:17 +0000622 capturedDiags.reportDiagnostics(*Diags);
623 DiagClient->EndSourceFile();
Jordan Roseb00073d2012-08-10 01:06:16 +0000624 errRec.FinishCapture();
John McCalld70fb982011-06-15 23:25:17 +0000625 return true;
626 }
627
628 // After parsing of source files ended, we want to reuse the
629 // diagnostics objects to emit further diagnostics.
David Blaikiee2eefae2011-09-25 23:39:51 +0000630 // We call BeginSourceFile because DiagnosticConsumer requires that
John McCalld70fb982011-06-15 23:25:17 +0000631 // diagnostics with source range information are emitted only in between
632 // BeginSourceFile() and EndSourceFile().
David Blaikiebbafb8a2012-03-11 07:00:24 +0000633 DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor());
John McCalld70fb982011-06-15 23:25:17 +0000634
David Blaikiebbafb8a2012-03-11 07:00:24 +0000635 Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
John McCalld70fb982011-06-15 23:25:17 +0000636 TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
Ted Kremenek8cf47df2011-11-17 23:01:24 +0000637 MigrationPass pass(Ctx, OrigCI.getLangOpts()->getGC(),
Argyrios Kyrtzidisd208ef92011-11-04 15:58:08 +0000638 Unit->getSema(), TA, ARCMTMacroLocs);
John McCalld70fb982011-06-15 23:25:17 +0000639
640 trans(pass);
641
642 {
643 RewritesApplicator applicator(rewriter, Ctx, listener);
644 TA.applyRewrites(applicator);
645 }
646
647 DiagClient->EndSourceFile();
Jordan Roseb00073d2012-08-10 01:06:16 +0000648 errRec.FinishCapture();
John McCalld70fb982011-06-15 23:25:17 +0000649
650 if (DiagClient->getNumErrors())
651 return true;
652
653 for (Rewriter::buffer_iterator
654 I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
655 FileID FID = I->first;
656 RewriteBuffer &buf = I->second;
657 const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
658 assert(file);
659 std::string newFname = file->getName();
660 newFname += "-trans";
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000661 SmallString<512> newText;
John McCalld70fb982011-06-15 23:25:17 +0000662 llvm::raw_svector_ostream vecOS(newText);
663 buf.write(vecOS);
664 vecOS.flush();
665 llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000666 StringRef(newText.data(), newText.size()), newFname);
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000667 SmallString<64> filePath(file->getName());
John McCalld70fb982011-06-15 23:25:17 +0000668 Unit->getFileManager().FixupRelativePath(filePath);
669 Remapper.remap(filePath.str(), memBuf);
670 }
671
672 return false;
673}