blob: f35f2577edc37d38a69302cb79eeabf949e31bfe [file] [log] [blame]
John McCall8f0e8d22011-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"
Argyrios Kyrtzidis7ee20492011-07-19 17:20:03 +000013#include "clang/Frontend/TextDiagnosticPrinter.h"
John McCall8f0e8d22011-06-15 23:25:17 +000014#include "clang/Frontend/Utils.h"
15#include "clang/AST/ASTConsumer.h"
16#include "clang/Rewrite/Rewriter.h"
17#include "clang/Sema/SemaDiagnostic.h"
18#include "clang/Basic/DiagnosticCategories.h"
19#include "clang/Lex/Preprocessor.h"
20#include "llvm/Support/MemoryBuffer.h"
21#include "llvm/ADT/Triple.h"
John McCall8f0e8d22011-06-15 23:25:17 +000022using namespace clang;
23using namespace arcmt;
John McCall8f0e8d22011-06-15 23:25:17 +000024
Chris Lattner2d3ba4f2011-07-23 17:14:25 +000025bool CapturedDiagList::clearDiagnostic(ArrayRef<unsigned> IDs,
John McCall8f0e8d22011-06-15 23:25:17 +000026 SourceRange range) {
27 if (range.isInvalid())
28 return false;
29
30 bool cleared = false;
31 ListTy::iterator I = List.begin();
32 while (I != List.end()) {
33 FullSourceLoc diagLoc = I->getLocation();
34 if ((IDs.empty() || // empty means clear all diagnostics in the range.
35 std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) &&
36 !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
37 (diagLoc == range.getEnd() ||
38 diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
39 cleared = true;
40 ListTy::iterator eraseS = I++;
David Blaikied6471f72011-09-25 23:23:43 +000041 while (I != List.end() && I->getLevel() == DiagnosticsEngine::Note)
John McCall8f0e8d22011-06-15 23:25:17 +000042 ++I;
43 // Clear the diagnostic and any notes following it.
44 List.erase(eraseS, I);
45 continue;
46 }
47
48 ++I;
49 }
50
51 return cleared;
52}
53
Chris Lattner2d3ba4f2011-07-23 17:14:25 +000054bool CapturedDiagList::hasDiagnostic(ArrayRef<unsigned> IDs,
Argyrios Kyrtzidis60a5e3f2011-06-18 00:53:34 +000055 SourceRange range) const {
John McCall8f0e8d22011-06-15 23:25:17 +000056 if (range.isInvalid())
57 return false;
58
Argyrios Kyrtzidis60a5e3f2011-06-18 00:53:34 +000059 ListTy::const_iterator I = List.begin();
John McCall8f0e8d22011-06-15 23:25:17 +000060 while (I != List.end()) {
61 FullSourceLoc diagLoc = I->getLocation();
62 if ((IDs.empty() || // empty means any diagnostic in the range.
63 std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) &&
64 !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) &&
65 (diagLoc == range.getEnd() ||
66 diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) {
67 return true;
68 }
69
70 ++I;
71 }
72
73 return false;
74}
75
David Blaikied6471f72011-09-25 23:23:43 +000076void CapturedDiagList::reportDiagnostics(DiagnosticsEngine &Diags) const {
Argyrios Kyrtzidis60a5e3f2011-06-18 00:53:34 +000077 for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
John McCall8f0e8d22011-06-15 23:25:17 +000078 Diags.Report(*I);
79}
80
Argyrios Kyrtzidise665d692011-06-18 00:53:41 +000081bool CapturedDiagList::hasErrors() const {
82 for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I)
David Blaikied6471f72011-09-25 23:23:43 +000083 if (I->getLevel() >= DiagnosticsEngine::Error)
Argyrios Kyrtzidise665d692011-06-18 00:53:41 +000084 return true;
85
86 return false;
87}
88
John McCall8f0e8d22011-06-15 23:25:17 +000089namespace {
90
David Blaikieaee37ba2011-09-25 23:54:33 +000091class CaptureDiagnosticConsumer : public DiagnosticConsumer {
David Blaikied6471f72011-09-25 23:23:43 +000092 DiagnosticsEngine &Diags;
John McCall8f0e8d22011-06-15 23:25:17 +000093 CapturedDiagList &CapturedDiags;
94public:
David Blaikieaee37ba2011-09-25 23:54:33 +000095 CaptureDiagnosticConsumer(DiagnosticsEngine &diags,
Douglas Gregoraee526e2011-09-29 00:38:00 +000096 CapturedDiagList &capturedDiags)
John McCall8f0e8d22011-06-15 23:25:17 +000097 : Diags(diags), CapturedDiags(capturedDiags) { }
98
David Blaikied6471f72011-09-25 23:23:43 +000099 virtual void HandleDiagnostic(DiagnosticsEngine::Level level,
David Blaikie40847cf2011-09-26 01:18:08 +0000100 const Diagnostic &Info) {
Ted Kremenekafdc21a2011-10-20 05:07:47 +0000101 if (DiagnosticIDs::isARCDiagnostic(Info.getID()) ||
David Blaikied6471f72011-09-25 23:23:43 +0000102 level >= DiagnosticsEngine::Error || level == DiagnosticsEngine::Note) {
John McCall8f0e8d22011-06-15 23:25:17 +0000103 CapturedDiags.push_back(StoredDiagnostic(level, Info));
104 return;
105 }
106
107 // Non-ARC warnings are ignored.
108 Diags.setLastDiagnosticIgnored();
109 }
Douglas Gregoraee526e2011-09-29 00:38:00 +0000110
111 DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const {
112 // Just drop any diagnostics that come from cloned consumers; they'll
113 // have different source managers anyway.
114 return new IgnoringDiagConsumer();
115 }
John McCall8f0e8d22011-06-15 23:25:17 +0000116};
117
118} // end anonymous namespace
119
Argyrios Kyrtzidisdceb11f2011-10-18 00:22:49 +0000120static inline StringRef SimulatorVersionDefineName() {
121 return "__IPHONE_OS_VERSION_MIN_REQUIRED=";
122}
123
124/// \brief Parse the simulator version define:
125/// __IPHONE_OS_VERSION_MIN_REQUIRED=([0-9])([0-9][0-9])([0-9][0-9])
126// and return the grouped values as integers, e.g:
127// __IPHONE_OS_VERSION_MIN_REQUIRED=40201
128// will return Major=4, Minor=2, Micro=1.
129static bool GetVersionFromSimulatorDefine(StringRef define,
130 unsigned &Major, unsigned &Minor,
131 unsigned &Micro) {
132 assert(define.startswith(SimulatorVersionDefineName()));
133 StringRef name, version;
134 llvm::tie(name, version) = define.split('=');
135 if (version.empty())
136 return false;
137 std::string verstr = version.str();
138 char *end;
139 unsigned num = (unsigned) strtol(verstr.c_str(), &end, 10);
140 if (*end != '\0')
141 return false;
142 Major = num / 10000;
143 num = num % 10000;
144 Minor = num / 100;
145 Micro = num % 100;
146 return true;
147}
148
Argyrios Kyrtzidis9c479732011-06-20 19:59:52 +0000149static bool HasARCRuntime(CompilerInvocation &origCI) {
150 // This duplicates some functionality from Darwin::AddDeploymentTarget
151 // but this function is well defined, so keep it decoupled from the driver
152 // and avoid unrelated complications.
153
Argyrios Kyrtzidisdceb11f2011-10-18 00:22:49 +0000154 for (unsigned i = 0, e = origCI.getPreprocessorOpts().Macros.size();
155 i != e; ++i) {
156 StringRef define = origCI.getPreprocessorOpts().Macros[i].first;
157 bool isUndef = origCI.getPreprocessorOpts().Macros[i].second;
158 if (isUndef)
159 continue;
160 if (!define.startswith(SimulatorVersionDefineName()))
161 continue;
162 unsigned Major = 0, Minor = 0, Micro = 0;
163 if (GetVersionFromSimulatorDefine(define, Major, Minor, Micro) &&
164 Major < 10 && Minor < 100 && Micro < 100)
165 return Major >= 5;
166 }
167
Argyrios Kyrtzidis9c479732011-06-20 19:59:52 +0000168 llvm::Triple triple(origCI.getTargetOpts().Triple);
169
170 if (triple.getOS() == llvm::Triple::IOS)
171 return triple.getOSMajorVersion() >= 5;
172
173 if (triple.getOS() == llvm::Triple::Darwin)
174 return triple.getOSMajorVersion() >= 11;
175
176 if (triple.getOS() == llvm::Triple::MacOSX) {
177 unsigned Major, Minor, Micro;
178 triple.getOSVersion(Major, Minor, Micro);
179 return Major > 10 || (Major == 10 && Minor >= 7);
180 }
181
182 return false;
183}
184
Benjamin Kramer39997fc2011-08-02 04:50:49 +0000185static CompilerInvocation *
186createInvocationForMigration(CompilerInvocation &origCI) {
John McCall8f0e8d22011-06-15 23:25:17 +0000187 llvm::OwningPtr<CompilerInvocation> CInvok;
188 CInvok.reset(new CompilerInvocation(origCI));
189 CInvok->getPreprocessorOpts().ImplicitPCHInclude = std::string();
190 CInvok->getPreprocessorOpts().ImplicitPTHInclude = std::string();
191 std::string define = getARCMTMacroName();
192 define += '=';
193 CInvok->getPreprocessorOpts().addMacroDef(define);
194 CInvok->getLangOpts().ObjCAutoRefCount = true;
Argyrios Kyrtzidise0ac7452011-11-04 15:58:08 +0000195 CInvok->getLangOpts().setGC(LangOptions::NonGC);
John McCall8f0e8d22011-06-15 23:25:17 +0000196 CInvok->getDiagnosticOpts().ErrorLimit = 0;
Argyrios Kyrtzidis7bf952e2011-06-22 18:03:59 +0000197 CInvok->getDiagnosticOpts().Warnings.push_back(
198 "error=arc-unsafe-retained-assign");
John McCall9f084a32011-07-06 00:26:06 +0000199 CInvok->getLangOpts().ObjCRuntimeHasWeak = HasARCRuntime(origCI);
John McCall8f0e8d22011-06-15 23:25:17 +0000200
201 return CInvok.take();
202}
203
Benjamin Kramer39997fc2011-08-02 04:50:49 +0000204static void emitPremigrationErrors(const CapturedDiagList &arcDiags,
205 const DiagnosticOptions &diagOpts,
206 Preprocessor &PP) {
Argyrios Kyrtzidis7ee20492011-07-19 17:20:03 +0000207 TextDiagnosticPrinter printer(llvm::errs(), diagOpts);
208 llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
David Blaikied6471f72011-09-25 23:23:43 +0000209 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
210 new DiagnosticsEngine(DiagID, &printer, /*ShouldOwnClient=*/false));
Argyrios Kyrtzidis7ee20492011-07-19 17:20:03 +0000211 Diags->setSourceManager(&PP.getSourceManager());
212
213 printer.BeginSourceFile(PP.getLangOptions(), &PP);
214 arcDiags.reportDiagnostics(*Diags);
215 printer.EndSourceFile();
216}
217
John McCall8f0e8d22011-06-15 23:25:17 +0000218//===----------------------------------------------------------------------===//
219// checkForManualIssues.
220//===----------------------------------------------------------------------===//
221
222bool arcmt::checkForManualIssues(CompilerInvocation &origCI,
Chris Lattner5f9e2722011-07-23 10:55:15 +0000223 StringRef Filename, InputKind Kind,
David Blaikie78ad0b92011-09-25 23:39:51 +0000224 DiagnosticConsumer *DiagClient,
Argyrios Kyrtzidis7ee20492011-07-19 17:20:03 +0000225 bool emitPremigrationARCErrors,
Chris Lattner5f9e2722011-07-23 10:55:15 +0000226 StringRef plistOut) {
John McCall8f0e8d22011-06-15 23:25:17 +0000227 if (!origCI.getLangOpts().ObjC1)
228 return false;
229
Argyrios Kyrtzidise0ac7452011-11-04 15:58:08 +0000230 LangOptions::GCMode OrigGCMode = origCI.getLangOpts().getGC();
231
232 std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode);
John McCall8f0e8d22011-06-15 23:25:17 +0000233 assert(!transforms.empty());
234
235 llvm::OwningPtr<CompilerInvocation> CInvok;
236 CInvok.reset(createInvocationForMigration(origCI));
237 CInvok->getFrontendOpts().Inputs.clear();
238 CInvok->getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename));
239
240 CapturedDiagList capturedDiags;
241
242 assert(DiagClient);
243 llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
David Blaikied6471f72011-09-25 23:23:43 +0000244 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
245 new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
John McCall8f0e8d22011-06-15 23:25:17 +0000246
247 // Filter of all diagnostics.
David Blaikieaee37ba2011-09-25 23:54:33 +0000248 CaptureDiagnosticConsumer errRec(*Diags, capturedDiags);
John McCall8f0e8d22011-06-15 23:25:17 +0000249 Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
250
251 llvm::OwningPtr<ASTUnit> Unit(
252 ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags));
253 if (!Unit)
254 return true;
255
256 // Don't filter diagnostics anymore.
257 Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
258
259 ASTContext &Ctx = Unit->getASTContext();
260
261 if (Diags->hasFatalErrorOccurred()) {
262 Diags->Reset();
263 DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
264 capturedDiags.reportDiagnostics(*Diags);
265 DiagClient->EndSourceFile();
266 return true;
267 }
268
Argyrios Kyrtzidis7ee20492011-07-19 17:20:03 +0000269 if (emitPremigrationARCErrors)
270 emitPremigrationErrors(capturedDiags, origCI.getDiagnosticOpts(),
271 Unit->getPreprocessor());
272 if (!plistOut.empty()) {
Chris Lattner5f9e2722011-07-23 10:55:15 +0000273 SmallVector<StoredDiagnostic, 8> arcDiags;
Argyrios Kyrtzidis7ee20492011-07-19 17:20:03 +0000274 for (CapturedDiagList::iterator
275 I = capturedDiags.begin(), E = capturedDiags.end(); I != E; ++I)
276 arcDiags.push_back(*I);
277 writeARCDiagsToPlist(plistOut, arcDiags,
278 Ctx.getSourceManager(), Ctx.getLangOptions());
279 }
280
John McCall8f0e8d22011-06-15 23:25:17 +0000281 // After parsing of source files ended, we want to reuse the
282 // diagnostics objects to emit further diagnostics.
David Blaikie78ad0b92011-09-25 23:39:51 +0000283 // We call BeginSourceFile because DiagnosticConsumer requires that
John McCall8f0e8d22011-06-15 23:25:17 +0000284 // diagnostics with source range information are emitted only in between
285 // BeginSourceFile() and EndSourceFile().
286 DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
287
288 // No macros will be added since we are just checking and we won't modify
289 // source code.
290 std::vector<SourceLocation> ARCMTMacroLocs;
291
292 TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
Argyrios Kyrtzidise0ac7452011-11-04 15:58:08 +0000293 MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, ARCMTMacroLocs);
John McCall8f0e8d22011-06-15 23:25:17 +0000294
295 for (unsigned i=0, e = transforms.size(); i != e; ++i)
296 transforms[i](pass);
297
298 capturedDiags.reportDiagnostics(*Diags);
299
300 DiagClient->EndSourceFile();
301
Argyrios Kyrtzidisa944b122011-07-14 00:17:54 +0000302 // If we are migrating code that gets the '-fobjc-arc' flag, make sure
303 // to remove it so that we don't get errors from normal compilation.
304 origCI.getLangOpts().ObjCAutoRefCount = false;
305
Argyrios Kyrtzidisfd103982011-07-18 07:44:45 +0000306 return capturedDiags.hasErrors() || testAct.hasReportedErrors();
John McCall8f0e8d22011-06-15 23:25:17 +0000307}
308
309//===----------------------------------------------------------------------===//
310// applyTransformations.
311//===----------------------------------------------------------------------===//
312
Argyrios Kyrtzidis69325d52011-07-09 20:00:58 +0000313static bool applyTransforms(CompilerInvocation &origCI,
Chris Lattner5f9e2722011-07-23 10:55:15 +0000314 StringRef Filename, InputKind Kind,
David Blaikie78ad0b92011-09-25 23:39:51 +0000315 DiagnosticConsumer *DiagClient,
Chris Lattner5f9e2722011-07-23 10:55:15 +0000316 StringRef outputDir,
Argyrios Kyrtzidis7ee20492011-07-19 17:20:03 +0000317 bool emitPremigrationARCErrors,
Chris Lattner5f9e2722011-07-23 10:55:15 +0000318 StringRef plistOut) {
John McCall8f0e8d22011-06-15 23:25:17 +0000319 if (!origCI.getLangOpts().ObjC1)
320 return false;
321
Argyrios Kyrtzidise0ac7452011-11-04 15:58:08 +0000322 LangOptions::GCMode OrigGCMode = origCI.getLangOpts().getGC();
323
John McCall8f0e8d22011-06-15 23:25:17 +0000324 // Make sure checking is successful first.
325 CompilerInvocation CInvokForCheck(origCI);
Argyrios Kyrtzidis7ee20492011-07-19 17:20:03 +0000326 if (arcmt::checkForManualIssues(CInvokForCheck, Filename, Kind, DiagClient,
327 emitPremigrationARCErrors, plistOut))
John McCall8f0e8d22011-06-15 23:25:17 +0000328 return true;
329
330 CompilerInvocation CInvok(origCI);
331 CInvok.getFrontendOpts().Inputs.clear();
332 CInvok.getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename));
333
Argyrios Kyrtzidis69325d52011-07-09 20:00:58 +0000334 MigrationProcess migration(CInvok, DiagClient, outputDir);
John McCall8f0e8d22011-06-15 23:25:17 +0000335
Argyrios Kyrtzidise0ac7452011-11-04 15:58:08 +0000336 std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode);
John McCall8f0e8d22011-06-15 23:25:17 +0000337 assert(!transforms.empty());
338
339 for (unsigned i=0, e = transforms.size(); i != e; ++i) {
340 bool err = migration.applyTransform(transforms[i]);
341 if (err) return true;
342 }
343
Argyrios Kyrtzidis69325d52011-07-09 20:00:58 +0000344 llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
David Blaikied6471f72011-09-25 23:23:43 +0000345 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
346 new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
Argyrios Kyrtzidis69325d52011-07-09 20:00:58 +0000347
348 if (outputDir.empty()) {
349 origCI.getLangOpts().ObjCAutoRefCount = true;
350 return migration.getRemapper().overwriteOriginal(*Diags);
Argyrios Kyrtzidisa944b122011-07-14 00:17:54 +0000351 } else {
352 // 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.
354 origCI.getLangOpts().ObjCAutoRefCount = false;
Argyrios Kyrtzidis69325d52011-07-09 20:00:58 +0000355 return migration.getRemapper().flushToDisk(outputDir, *Diags);
Argyrios Kyrtzidisa944b122011-07-14 00:17:54 +0000356 }
Argyrios Kyrtzidis69325d52011-07-09 20:00:58 +0000357}
358
359bool arcmt::applyTransformations(CompilerInvocation &origCI,
Chris Lattner5f9e2722011-07-23 10:55:15 +0000360 StringRef Filename, InputKind Kind,
David Blaikie78ad0b92011-09-25 23:39:51 +0000361 DiagnosticConsumer *DiagClient) {
Argyrios Kyrtzidis7ee20492011-07-19 17:20:03 +0000362 return applyTransforms(origCI, Filename, Kind, DiagClient,
Chris Lattner5f9e2722011-07-23 10:55:15 +0000363 StringRef(), false, StringRef());
Argyrios Kyrtzidis69325d52011-07-09 20:00:58 +0000364}
365
366bool arcmt::migrateWithTemporaryFiles(CompilerInvocation &origCI,
Chris Lattner5f9e2722011-07-23 10:55:15 +0000367 StringRef Filename, InputKind Kind,
David Blaikie78ad0b92011-09-25 23:39:51 +0000368 DiagnosticConsumer *DiagClient,
Chris Lattner5f9e2722011-07-23 10:55:15 +0000369 StringRef outputDir,
Argyrios Kyrtzidis7ee20492011-07-19 17:20:03 +0000370 bool emitPremigrationARCErrors,
Chris Lattner5f9e2722011-07-23 10:55:15 +0000371 StringRef plistOut) {
Argyrios Kyrtzidis69325d52011-07-09 20:00:58 +0000372 assert(!outputDir.empty() && "Expected output directory path");
Argyrios Kyrtzidis7ee20492011-07-19 17:20:03 +0000373 return applyTransforms(origCI, Filename, Kind, DiagClient,
374 outputDir, emitPremigrationARCErrors, plistOut);
Argyrios Kyrtzidis69325d52011-07-09 20:00:58 +0000375}
376
377bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > &
378 remap,
Chris Lattner5f9e2722011-07-23 10:55:15 +0000379 StringRef outputDir,
David Blaikie78ad0b92011-09-25 23:39:51 +0000380 DiagnosticConsumer *DiagClient) {
Argyrios Kyrtzidis69325d52011-07-09 20:00:58 +0000381 assert(!outputDir.empty());
John McCall8f0e8d22011-06-15 23:25:17 +0000382
383 llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
David Blaikied6471f72011-09-25 23:23:43 +0000384 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
385 new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
Argyrios Kyrtzidis69325d52011-07-09 20:00:58 +0000386
387 FileRemapper remapper;
388 bool err = remapper.initFromDisk(outputDir, *Diags,
389 /*ignoreIfFilesChanged=*/true);
390 if (err)
391 return true;
392
393 CompilerInvocation CI;
394 remapper.applyMappings(CI);
395 remap = CI.getPreprocessorOpts().RemappedFiles;
396
397 return false;
John McCall8f0e8d22011-06-15 23:25:17 +0000398}
399
400//===----------------------------------------------------------------------===//
John McCall8f0e8d22011-06-15 23:25:17 +0000401// CollectTransformActions.
402//===----------------------------------------------------------------------===//
403
404namespace {
405
406class ARCMTMacroTrackerPPCallbacks : public PPCallbacks {
407 std::vector<SourceLocation> &ARCMTMacroLocs;
408
409public:
410 ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs)
411 : ARCMTMacroLocs(ARCMTMacroLocs) { }
412
Argyrios Kyrtzidis1b2d5362011-08-18 01:05:45 +0000413 virtual void MacroExpands(const Token &MacroNameTok, const MacroInfo *MI,
414 SourceRange Range) {
John McCall8f0e8d22011-06-15 23:25:17 +0000415 if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName())
416 ARCMTMacroLocs.push_back(MacroNameTok.getLocation());
417 }
418};
419
420class ARCMTMacroTrackerAction : public ASTFrontendAction {
421 std::vector<SourceLocation> &ARCMTMacroLocs;
422
423public:
424 ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs)
425 : ARCMTMacroLocs(ARCMTMacroLocs) { }
426
427 virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
Chris Lattner5f9e2722011-07-23 10:55:15 +0000428 StringRef InFile) {
John McCall8f0e8d22011-06-15 23:25:17 +0000429 CI.getPreprocessor().addPPCallbacks(
430 new ARCMTMacroTrackerPPCallbacks(ARCMTMacroLocs));
431 return new ASTConsumer();
432 }
433};
434
435class RewritesApplicator : public TransformActions::RewriteReceiver {
436 Rewriter &rewriter;
437 ASTContext &Ctx;
438 MigrationProcess::RewriteListener *Listener;
439
440public:
441 RewritesApplicator(Rewriter &rewriter, ASTContext &ctx,
442 MigrationProcess::RewriteListener *listener)
443 : rewriter(rewriter), Ctx(ctx), Listener(listener) {
444 if (Listener)
445 Listener->start(ctx);
446 }
447 ~RewritesApplicator() {
448 if (Listener)
449 Listener->finish();
450 }
451
Chris Lattner5f9e2722011-07-23 10:55:15 +0000452 virtual void insert(SourceLocation loc, StringRef text) {
John McCall8f0e8d22011-06-15 23:25:17 +0000453 bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true,
454 /*indentNewLines=*/true);
455 if (!err && Listener)
456 Listener->insert(loc, text);
457 }
458
459 virtual void remove(CharSourceRange range) {
460 Rewriter::RewriteOptions removeOpts;
461 removeOpts.IncludeInsertsAtBeginOfRange = false;
462 removeOpts.IncludeInsertsAtEndOfRange = false;
463 removeOpts.RemoveLineIfEmpty = true;
464
465 bool err = rewriter.RemoveText(range, removeOpts);
466 if (!err && Listener)
467 Listener->remove(range);
468 }
469
470 virtual void increaseIndentation(CharSourceRange range,
471 SourceLocation parentIndent) {
472 rewriter.IncreaseIndentation(range, parentIndent);
473 }
474};
475
476} // end anonymous namespace.
477
478/// \brief Anchor for VTable.
479MigrationProcess::RewriteListener::~RewriteListener() { }
480
Argyrios Kyrtzidis69325d52011-07-09 20:00:58 +0000481MigrationProcess::MigrationProcess(const CompilerInvocation &CI,
David Blaikie78ad0b92011-09-25 23:39:51 +0000482 DiagnosticConsumer *diagClient,
Chris Lattner5f9e2722011-07-23 10:55:15 +0000483 StringRef outputDir)
Argyrios Kyrtzidis69325d52011-07-09 20:00:58 +0000484 : OrigCI(CI), DiagClient(diagClient) {
485 if (!outputDir.empty()) {
486 llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
David Blaikied6471f72011-09-25 23:23:43 +0000487 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
488 new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
Argyrios Kyrtzidis69325d52011-07-09 20:00:58 +0000489 Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true);
490 }
491}
492
John McCall8f0e8d22011-06-15 23:25:17 +0000493bool MigrationProcess::applyTransform(TransformFn trans,
494 RewriteListener *listener) {
495 llvm::OwningPtr<CompilerInvocation> CInvok;
496 CInvok.reset(createInvocationForMigration(OrigCI));
497 CInvok->getDiagnosticOpts().IgnoreWarnings = true;
498
499 Remapper.applyMappings(*CInvok);
500
501 CapturedDiagList capturedDiags;
502 std::vector<SourceLocation> ARCMTMacroLocs;
503
504 assert(DiagClient);
505 llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
David Blaikied6471f72011-09-25 23:23:43 +0000506 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
507 new DiagnosticsEngine(DiagID, DiagClient, /*ShouldOwnClient=*/false));
John McCall8f0e8d22011-06-15 23:25:17 +0000508
509 // Filter of all diagnostics.
David Blaikieaee37ba2011-09-25 23:54:33 +0000510 CaptureDiagnosticConsumer errRec(*Diags, capturedDiags);
John McCall8f0e8d22011-06-15 23:25:17 +0000511 Diags->setClient(&errRec, /*ShouldOwnClient=*/false);
512
513 llvm::OwningPtr<ARCMTMacroTrackerAction> ASTAction;
514 ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs));
515
516 llvm::OwningPtr<ASTUnit> Unit(
517 ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags,
518 ASTAction.get()));
519 if (!Unit)
520 return true;
521 Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that.
522
523 // Don't filter diagnostics anymore.
524 Diags->setClient(DiagClient, /*ShouldOwnClient=*/false);
525
526 ASTContext &Ctx = Unit->getASTContext();
527
528 if (Diags->hasFatalErrorOccurred()) {
529 Diags->Reset();
530 DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
531 capturedDiags.reportDiagnostics(*Diags);
532 DiagClient->EndSourceFile();
533 return true;
534 }
535
536 // After parsing of source files ended, we want to reuse the
537 // diagnostics objects to emit further diagnostics.
David Blaikie78ad0b92011-09-25 23:39:51 +0000538 // We call BeginSourceFile because DiagnosticConsumer requires that
John McCall8f0e8d22011-06-15 23:25:17 +0000539 // diagnostics with source range information are emitted only in between
540 // BeginSourceFile() and EndSourceFile().
541 DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor());
542
543 Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOptions());
544 TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor());
Argyrios Kyrtzidise0ac7452011-11-04 15:58:08 +0000545 MigrationPass pass(Ctx, OrigCI.getLangOpts().getGC(),
546 Unit->getSema(), TA, ARCMTMacroLocs);
John McCall8f0e8d22011-06-15 23:25:17 +0000547
548 trans(pass);
549
550 {
551 RewritesApplicator applicator(rewriter, Ctx, listener);
552 TA.applyRewrites(applicator);
553 }
554
555 DiagClient->EndSourceFile();
556
557 if (DiagClient->getNumErrors())
558 return true;
559
560 for (Rewriter::buffer_iterator
561 I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
562 FileID FID = I->first;
563 RewriteBuffer &buf = I->second;
564 const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
565 assert(file);
566 std::string newFname = file->getName();
567 newFname += "-trans";
568 llvm::SmallString<512> newText;
569 llvm::raw_svector_ostream vecOS(newText);
570 buf.write(vecOS);
571 vecOS.flush();
572 llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
Chris Lattner5f9e2722011-07-23 10:55:15 +0000573 StringRef(newText.data(), newText.size()), newFname);
John McCall8f0e8d22011-06-15 23:25:17 +0000574 llvm::SmallString<64> filePath(file->getName());
575 Unit->getFileManager().FixupRelativePath(filePath);
576 Remapper.remap(filePath.str(), memBuf);
577 }
578
579 return false;
580}