/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/tools/clang/lib/ARCMigrate/ARCMT.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- 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/AST/ASTConsumer.h" |
12 | | #include "clang/Basic/DiagnosticCategories.h" |
13 | | #include "clang/Frontend/ASTUnit.h" |
14 | | #include "clang/Frontend/CompilerInstance.h" |
15 | | #include "clang/Frontend/FrontendAction.h" |
16 | | #include "clang/Frontend/TextDiagnosticPrinter.h" |
17 | | #include "clang/Frontend/Utils.h" |
18 | | #include "clang/Lex/Preprocessor.h" |
19 | | #include "clang/Lex/PreprocessorOptions.h" |
20 | | #include "clang/Rewrite/Core/Rewriter.h" |
21 | | #include "clang/Sema/SemaDiagnostic.h" |
22 | | #include "clang/Serialization/ASTReader.h" |
23 | | #include "llvm/ADT/Triple.h" |
24 | | #include "llvm/Support/MemoryBuffer.h" |
25 | | #include <utility> |
26 | | using namespace clang; |
27 | | using namespace arcmt; |
28 | | |
29 | | bool CapturedDiagList::clearDiagnostic(ArrayRef<unsigned> IDs, |
30 | 484 | SourceRange range) { |
31 | 484 | if (range.isInvalid()) |
32 | 0 | return false; |
33 | 484 | |
34 | 484 | bool cleared = false; |
35 | 484 | ListTy::iterator I = List.begin(); |
36 | 8.89k | while (I != List.end()8.89k ) { |
37 | 8.41k | FullSourceLoc diagLoc = I->getLocation(); |
38 | 8.41k | if ((IDs.empty() || // empty means clear all diagnostics in the range. |
39 | 8.41k | std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) && |
40 | 3.63k | !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) && |
41 | 3.30k | (diagLoc == range.getEnd() || |
42 | 8.41k | diagLoc.isBeforeInTranslationUnitThan(range.getEnd())2.74k )) { |
43 | 621 | cleared = true; |
44 | 621 | ListTy::iterator eraseS = I++; |
45 | 621 | if (eraseS->getLevel() != DiagnosticsEngine::Note) |
46 | 970 | while (596 I != List.end() && 970 I->getLevel() == DiagnosticsEngine::Note884 ) |
47 | 374 | ++I; |
48 | 621 | // Clear the diagnostic and any notes following it. |
49 | 621 | I = List.erase(eraseS, I); |
50 | 621 | continue; |
51 | 621 | } |
52 | 7.79k | |
53 | 7.79k | ++I; |
54 | 7.79k | } |
55 | 484 | |
56 | 484 | return cleared; |
57 | 484 | } |
58 | | |
59 | | bool CapturedDiagList::hasDiagnostic(ArrayRef<unsigned> IDs, |
60 | 810 | SourceRange range) const { |
61 | 810 | if (range.isInvalid()) |
62 | 0 | return false; |
63 | 810 | |
64 | 810 | ListTy::const_iterator I = List.begin(); |
65 | 5.56k | while (I != List.end()5.56k ) { |
66 | 5.30k | FullSourceLoc diagLoc = I->getLocation(); |
67 | 5.30k | if ((IDs.empty() || // empty means any diagnostic in the range. |
68 | 5.30k | std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) && |
69 | 1.33k | !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) && |
70 | 876 | (diagLoc == range.getEnd() || |
71 | 5.30k | diagLoc.isBeforeInTranslationUnitThan(range.getEnd())382 )) { |
72 | 550 | return true; |
73 | 550 | } |
74 | 4.75k | |
75 | 4.75k | ++I; |
76 | 4.75k | } |
77 | 810 | |
78 | 260 | return false; |
79 | 810 | } |
80 | | |
81 | 51 | void CapturedDiagList::reportDiagnostics(DiagnosticsEngine &Diags) const { |
82 | 202 | for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E202 ; ++I151 ) |
83 | 151 | Diags.Report(*I); |
84 | 51 | } |
85 | | |
86 | 88 | bool CapturedDiagList::hasErrors() const { |
87 | 88 | for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E88 ; ++I0 ) |
88 | 43 | if (43 I->getLevel() >= DiagnosticsEngine::Error43 ) |
89 | 43 | return true; |
90 | 88 | |
91 | 45 | return false; |
92 | 88 | } |
93 | | |
94 | | namespace { |
95 | | |
96 | | class CaptureDiagnosticConsumer : public DiagnosticConsumer { |
97 | | DiagnosticsEngine &Diags; |
98 | | DiagnosticConsumer &DiagClient; |
99 | | CapturedDiagList &CapturedDiags; |
100 | | bool HasBegunSourceFile; |
101 | | public: |
102 | | CaptureDiagnosticConsumer(DiagnosticsEngine &diags, |
103 | | DiagnosticConsumer &client, |
104 | | CapturedDiagList &capturedDiags) |
105 | | : Diags(diags), DiagClient(client), CapturedDiags(capturedDiags), |
106 | 122 | HasBegunSourceFile(false) { } |
107 | | |
108 | | void BeginSourceFile(const LangOptions &Opts, |
109 | 122 | const Preprocessor *PP) override { |
110 | 122 | // Pass BeginSourceFile message onto DiagClient on first call. |
111 | 122 | // The corresponding EndSourceFile call will be made from an |
112 | 122 | // explicit call to FinishCapture. |
113 | 122 | if (!HasBegunSourceFile122 ) { |
114 | 122 | DiagClient.BeginSourceFile(Opts, PP); |
115 | 122 | HasBegunSourceFile = true; |
116 | 122 | } |
117 | 122 | } |
118 | | |
119 | 122 | void FinishCapture() { |
120 | 122 | // Call EndSourceFile on DiagClient on completion of capture to |
121 | 122 | // enable VerifyDiagnosticConsumer to check diagnostics *after* |
122 | 122 | // it has received the diagnostic list. |
123 | 122 | if (HasBegunSourceFile122 ) { |
124 | 122 | DiagClient.EndSourceFile(); |
125 | 122 | HasBegunSourceFile = false; |
126 | 122 | } |
127 | 122 | } |
128 | | |
129 | 122 | ~CaptureDiagnosticConsumer() override { |
130 | 122 | assert(!HasBegunSourceFile && "FinishCapture not called!"); |
131 | 122 | } |
132 | | |
133 | | void HandleDiagnostic(DiagnosticsEngine::Level level, |
134 | 1.26k | const Diagnostic &Info) override { |
135 | 1.26k | if (DiagnosticIDs::isARCDiagnostic(Info.getID()) || |
136 | 1.26k | level >= DiagnosticsEngine::Error590 || level == DiagnosticsEngine::Note367 ) { |
137 | 1.18k | if (Info.getLocation().isValid()) |
138 | 1.18k | CapturedDiags.push_back(StoredDiagnostic(level, Info)); |
139 | 1.18k | return; |
140 | 1.18k | } |
141 | 82 | |
142 | 82 | // Non-ARC warnings are ignored. |
143 | 82 | Diags.setLastDiagnosticIgnored(); |
144 | 82 | } |
145 | | }; |
146 | | |
147 | | } // end anonymous namespace |
148 | | |
149 | 122 | static bool HasARCRuntime(CompilerInvocation &origCI) { |
150 | 122 | // This duplicates some functionality from Darwin::AddDeploymentTarget |
151 | 122 | // but this function is well defined, so keep it decoupled from the driver |
152 | 122 | // and avoid unrelated complications. |
153 | 122 | llvm::Triple triple(origCI.getTargetOpts().Triple); |
154 | 122 | |
155 | 122 | if (triple.isiOS()) |
156 | 0 | return triple.getOSMajorVersion() >= 5; |
157 | 122 | |
158 | 122 | if (122 triple.isWatchOS()122 ) |
159 | 0 | return true; |
160 | 122 | |
161 | 122 | if (122 triple.getOS() == llvm::Triple::Darwin122 ) |
162 | 94 | return triple.getOSMajorVersion() >= 11; |
163 | 28 | |
164 | 28 | if (28 triple.getOS() == llvm::Triple::MacOSX28 ) { |
165 | 28 | unsigned Major, Minor, Micro; |
166 | 28 | triple.getOSVersion(Major, Minor, Micro); |
167 | 28 | return Major > 10 || (Major == 10 && 28 Minor >= 728 ); |
168 | 28 | } |
169 | 0 |
|
170 | 0 | return false; |
171 | 0 | } |
172 | | |
173 | | static CompilerInvocation * |
174 | | createInvocationForMigration(CompilerInvocation &origCI, |
175 | 122 | const PCHContainerReader &PCHContainerRdr) { |
176 | 122 | std::unique_ptr<CompilerInvocation> CInvok; |
177 | 122 | CInvok.reset(new CompilerInvocation(origCI)); |
178 | 122 | PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts(); |
179 | 122 | if (!PPOpts.ImplicitPCHInclude.empty()122 ) { |
180 | 7 | // We can't use a PCH because it was likely built in non-ARC mode and we |
181 | 7 | // want to parse in ARC. Include the original header. |
182 | 7 | FileManager FileMgr(origCI.getFileSystemOpts()); |
183 | 7 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
184 | 7 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
185 | 7 | new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(), |
186 | 7 | new IgnoringDiagConsumer())); |
187 | 7 | std::string OriginalFile = ASTReader::getOriginalSourceFile( |
188 | 7 | PPOpts.ImplicitPCHInclude, FileMgr, PCHContainerRdr, *Diags); |
189 | 7 | if (!OriginalFile.empty()) |
190 | 7 | PPOpts.Includes.insert(PPOpts.Includes.begin(), OriginalFile); |
191 | 7 | PPOpts.ImplicitPCHInclude.clear(); |
192 | 7 | } |
193 | 122 | // FIXME: Get the original header of a PTH as well. |
194 | 122 | CInvok->getPreprocessorOpts().ImplicitPTHInclude.clear(); |
195 | 122 | std::string define = getARCMTMacroName(); |
196 | 122 | define += '='; |
197 | 122 | CInvok->getPreprocessorOpts().addMacroDef(define); |
198 | 122 | CInvok->getLangOpts()->ObjCAutoRefCount = true; |
199 | 122 | CInvok->getLangOpts()->setGC(LangOptions::NonGC); |
200 | 122 | CInvok->getDiagnosticOpts().ErrorLimit = 0; |
201 | 122 | CInvok->getDiagnosticOpts().PedanticErrors = 0; |
202 | 122 | |
203 | 122 | // Ignore -Werror flags when migrating. |
204 | 122 | std::vector<std::string> WarnOpts; |
205 | 122 | for (std::vector<std::string>::iterator |
206 | 122 | I = CInvok->getDiagnosticOpts().Warnings.begin(), |
207 | 125 | E = CInvok->getDiagnosticOpts().Warnings.end(); I != E125 ; ++I3 ) { |
208 | 3 | if (!StringRef(*I).startswith("error")) |
209 | 0 | WarnOpts.push_back(*I); |
210 | 3 | } |
211 | 122 | WarnOpts.push_back("error=arc-unsafe-retained-assign"); |
212 | 122 | CInvok->getDiagnosticOpts().Warnings = std::move(WarnOpts); |
213 | 122 | |
214 | 122 | CInvok->getLangOpts()->ObjCWeakRuntime = HasARCRuntime(origCI); |
215 | 122 | CInvok->getLangOpts()->ObjCWeak = CInvok->getLangOpts()->ObjCWeakRuntime; |
216 | 122 | |
217 | 122 | return CInvok.release(); |
218 | 122 | } |
219 | | |
220 | | static void emitPremigrationErrors(const CapturedDiagList &arcDiags, |
221 | | DiagnosticOptions *diagOpts, |
222 | 1 | Preprocessor &PP) { |
223 | 1 | TextDiagnosticPrinter printer(llvm::errs(), diagOpts); |
224 | 1 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
225 | 1 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
226 | 1 | new DiagnosticsEngine(DiagID, diagOpts, &printer, |
227 | 1 | /*ShouldOwnClient=*/false)); |
228 | 1 | Diags->setSourceManager(&PP.getSourceManager()); |
229 | 1 | |
230 | 1 | printer.BeginSourceFile(PP.getLangOpts(), &PP); |
231 | 1 | arcDiags.reportDiagnostics(*Diags); |
232 | 1 | printer.EndSourceFile(); |
233 | 1 | } |
234 | | |
235 | | //===----------------------------------------------------------------------===// |
236 | | // checkForManualIssues. |
237 | | //===----------------------------------------------------------------------===// |
238 | | |
239 | | bool arcmt::checkForManualIssues( |
240 | | CompilerInvocation &origCI, const FrontendInputFile &Input, |
241 | | std::shared_ptr<PCHContainerOperations> PCHContainerOps, |
242 | | DiagnosticConsumer *DiagClient, bool emitPremigrationARCErrors, |
243 | 51 | StringRef plistOut) { |
244 | 51 | if (!origCI.getLangOpts()->ObjC1) |
245 | 1 | return false; |
246 | 50 | |
247 | 50 | LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC(); |
248 | 50 | bool NoNSAllocReallocError = origCI.getMigratorOpts().NoNSAllocReallocError; |
249 | 50 | bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval; |
250 | 50 | |
251 | 50 | std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode, |
252 | 50 | NoFinalizeRemoval); |
253 | 50 | assert(!transforms.empty()); |
254 | 50 | |
255 | 50 | std::unique_ptr<CompilerInvocation> CInvok; |
256 | 50 | CInvok.reset( |
257 | 50 | createInvocationForMigration(origCI, PCHContainerOps->getRawReader())); |
258 | 50 | CInvok->getFrontendOpts().Inputs.clear(); |
259 | 50 | CInvok->getFrontendOpts().Inputs.push_back(Input); |
260 | 50 | |
261 | 50 | CapturedDiagList capturedDiags; |
262 | 50 | |
263 | 50 | assert(DiagClient); |
264 | 50 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
265 | 50 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
266 | 50 | new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(), |
267 | 50 | DiagClient, /*ShouldOwnClient=*/false)); |
268 | 50 | |
269 | 50 | // Filter of all diagnostics. |
270 | 50 | CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags); |
271 | 50 | Diags->setClient(&errRec, /*ShouldOwnClient=*/false); |
272 | 50 | |
273 | 50 | std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction( |
274 | 50 | std::move(CInvok), PCHContainerOps, Diags)); |
275 | 50 | if (!Unit50 ) { |
276 | 0 | errRec.FinishCapture(); |
277 | 0 | return true; |
278 | 0 | } |
279 | 50 | |
280 | 50 | // Don't filter diagnostics anymore. |
281 | 50 | Diags->setClient(DiagClient, /*ShouldOwnClient=*/false); |
282 | 50 | |
283 | 50 | ASTContext &Ctx = Unit->getASTContext(); |
284 | 50 | |
285 | 50 | if (Diags->hasFatalErrorOccurred()50 ) { |
286 | 0 | Diags->Reset(); |
287 | 0 | DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); |
288 | 0 | capturedDiags.reportDiagnostics(*Diags); |
289 | 0 | DiagClient->EndSourceFile(); |
290 | 0 | errRec.FinishCapture(); |
291 | 0 | return true; |
292 | 0 | } |
293 | 50 | |
294 | 50 | if (50 emitPremigrationARCErrors50 ) |
295 | 1 | emitPremigrationErrors(capturedDiags, &origCI.getDiagnosticOpts(), |
296 | 1 | Unit->getPreprocessor()); |
297 | 50 | if (!plistOut.empty()50 ) { |
298 | 1 | SmallVector<StoredDiagnostic, 8> arcDiags; |
299 | 1 | for (CapturedDiagList::iterator |
300 | 2 | I = capturedDiags.begin(), E = capturedDiags.end(); I != E2 ; ++I1 ) |
301 | 1 | arcDiags.push_back(*I); |
302 | 1 | writeARCDiagsToPlist(plistOut, arcDiags, |
303 | 1 | Ctx.getSourceManager(), Ctx.getLangOpts()); |
304 | 1 | } |
305 | 50 | |
306 | 50 | // After parsing of source files ended, we want to reuse the |
307 | 50 | // diagnostics objects to emit further diagnostics. |
308 | 50 | // We call BeginSourceFile because DiagnosticConsumer requires that |
309 | 50 | // diagnostics with source range information are emitted only in between |
310 | 50 | // BeginSourceFile() and EndSourceFile(). |
311 | 50 | DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); |
312 | 50 | |
313 | 50 | // No macros will be added since we are just checking and we won't modify |
314 | 50 | // source code. |
315 | 50 | std::vector<SourceLocation> ARCMTMacroLocs; |
316 | 50 | |
317 | 50 | TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor()); |
318 | 50 | MigrationPass pass(Ctx, OrigGCMode, Unit->getSema(), testAct, capturedDiags, |
319 | 50 | ARCMTMacroLocs); |
320 | 50 | pass.setNoFinalizeRemoval(NoFinalizeRemoval); |
321 | 50 | if (!NoNSAllocReallocError) |
322 | 48 | Diags->setSeverity(diag::warn_arcmt_nsalloc_realloc, diag::Severity::Error, |
323 | 48 | SourceLocation()); |
324 | 50 | |
325 | 152 | for (unsigned i=0, e = transforms.size(); i != e152 ; ++i102 ) |
326 | 102 | transforms[i](pass); |
327 | 50 | |
328 | 50 | capturedDiags.reportDiagnostics(*Diags); |
329 | 50 | |
330 | 50 | DiagClient->EndSourceFile(); |
331 | 50 | errRec.FinishCapture(); |
332 | 50 | |
333 | 39 | return capturedDiags.hasErrors() || testAct.hasReportedErrors(); |
334 | 51 | } |
335 | | |
336 | | //===----------------------------------------------------------------------===// |
337 | | // applyTransformations. |
338 | | //===----------------------------------------------------------------------===// |
339 | | |
340 | | static bool |
341 | | applyTransforms(CompilerInvocation &origCI, const FrontendInputFile &Input, |
342 | | std::shared_ptr<PCHContainerOperations> PCHContainerOps, |
343 | | DiagnosticConsumer *DiagClient, StringRef outputDir, |
344 | 10 | bool emitPremigrationARCErrors, StringRef plistOut) { |
345 | 10 | if (!origCI.getLangOpts()->ObjC1) |
346 | 0 | return false; |
347 | 10 | |
348 | 10 | LangOptions::GCMode OrigGCMode = origCI.getLangOpts()->getGC(); |
349 | 10 | |
350 | 10 | // Make sure checking is successful first. |
351 | 10 | CompilerInvocation CInvokForCheck(origCI); |
352 | 10 | if (arcmt::checkForManualIssues(CInvokForCheck, Input, PCHContainerOps, |
353 | 10 | DiagClient, emitPremigrationARCErrors, |
354 | 10 | plistOut)) |
355 | 0 | return true; |
356 | 10 | |
357 | 10 | CompilerInvocation CInvok(origCI); |
358 | 10 | CInvok.getFrontendOpts().Inputs.clear(); |
359 | 10 | CInvok.getFrontendOpts().Inputs.push_back(Input); |
360 | 10 | |
361 | 10 | MigrationProcess migration(CInvok, PCHContainerOps, DiagClient, outputDir); |
362 | 10 | bool NoFinalizeRemoval = origCI.getMigratorOpts().NoFinalizeRemoval; |
363 | 10 | |
364 | 10 | std::vector<TransformFn> transforms = arcmt::getAllTransformations(OrigGCMode, |
365 | 10 | NoFinalizeRemoval); |
366 | 10 | assert(!transforms.empty()); |
367 | 10 | |
368 | 30 | for (unsigned i=0, e = transforms.size(); i != e30 ; ++i20 ) { |
369 | 20 | bool err = migration.applyTransform(transforms[i]); |
370 | 20 | if (err20 ) return true0 ; |
371 | 20 | } |
372 | 10 | |
373 | 10 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
374 | 10 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
375 | 10 | new DiagnosticsEngine(DiagID, &origCI.getDiagnosticOpts(), |
376 | 10 | DiagClient, /*ShouldOwnClient=*/false)); |
377 | 10 | |
378 | 10 | if (outputDir.empty()10 ) { |
379 | 2 | origCI.getLangOpts()->ObjCAutoRefCount = true; |
380 | 2 | return migration.getRemapper().overwriteOriginal(*Diags); |
381 | 0 | } else { |
382 | 8 | return migration.getRemapper().flushToDisk(outputDir, *Diags); |
383 | 8 | } |
384 | 0 | } |
385 | | |
386 | | bool arcmt::applyTransformations( |
387 | | CompilerInvocation &origCI, const FrontendInputFile &Input, |
388 | | std::shared_ptr<PCHContainerOperations> PCHContainerOps, |
389 | 2 | DiagnosticConsumer *DiagClient) { |
390 | 2 | return applyTransforms(origCI, Input, PCHContainerOps, DiagClient, |
391 | 2 | StringRef(), false, StringRef()); |
392 | 2 | } |
393 | | |
394 | | bool arcmt::migrateWithTemporaryFiles( |
395 | | CompilerInvocation &origCI, const FrontendInputFile &Input, |
396 | | std::shared_ptr<PCHContainerOperations> PCHContainerOps, |
397 | | DiagnosticConsumer *DiagClient, StringRef outputDir, |
398 | 8 | bool emitPremigrationARCErrors, StringRef plistOut) { |
399 | 8 | assert(!outputDir.empty() && "Expected output directory path"); |
400 | 8 | return applyTransforms(origCI, Input, PCHContainerOps, DiagClient, outputDir, |
401 | 8 | emitPremigrationARCErrors, plistOut); |
402 | 8 | } |
403 | | |
404 | | bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > & |
405 | | remap, |
406 | | StringRef outputDir, |
407 | 24 | DiagnosticConsumer *DiagClient) { |
408 | 24 | assert(!outputDir.empty()); |
409 | 24 | |
410 | 24 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
411 | 24 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
412 | 24 | new DiagnosticsEngine(DiagID, new DiagnosticOptions, |
413 | 24 | DiagClient, /*ShouldOwnClient=*/false)); |
414 | 24 | |
415 | 24 | FileRemapper remapper; |
416 | 24 | bool err = remapper.initFromDisk(outputDir, *Diags, |
417 | 24 | /*ignoreIfFilesChanged=*/true); |
418 | 24 | if (err) |
419 | 0 | return true; |
420 | 24 | |
421 | 24 | PreprocessorOptions PPOpts; |
422 | 24 | remapper.applyMappings(PPOpts); |
423 | 24 | remap = PPOpts.RemappedFiles; |
424 | 24 | |
425 | 24 | return false; |
426 | 24 | } |
427 | | |
428 | | |
429 | | //===----------------------------------------------------------------------===// |
430 | | // CollectTransformActions. |
431 | | //===----------------------------------------------------------------------===// |
432 | | |
433 | | namespace { |
434 | | |
435 | | class ARCMTMacroTrackerPPCallbacks : public PPCallbacks { |
436 | | std::vector<SourceLocation> &ARCMTMacroLocs; |
437 | | |
438 | | public: |
439 | | ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs) |
440 | 72 | : ARCMTMacroLocs(ARCMTMacroLocs) { } |
441 | | |
442 | | void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, |
443 | 964 | SourceRange Range, const MacroArgs *Args) override { |
444 | 964 | if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName()) |
445 | 92 | ARCMTMacroLocs.push_back(MacroNameTok.getLocation()); |
446 | 964 | } |
447 | | }; |
448 | | |
449 | | class ARCMTMacroTrackerAction : public ASTFrontendAction { |
450 | | std::vector<SourceLocation> &ARCMTMacroLocs; |
451 | | |
452 | | public: |
453 | | ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs) |
454 | 72 | : ARCMTMacroLocs(ARCMTMacroLocs) { } |
455 | | |
456 | | std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, |
457 | 72 | StringRef InFile) override { |
458 | 72 | CI.getPreprocessor().addPPCallbacks( |
459 | 72 | llvm::make_unique<ARCMTMacroTrackerPPCallbacks>(ARCMTMacroLocs)); |
460 | 72 | return llvm::make_unique<ASTConsumer>(); |
461 | 72 | } |
462 | | }; |
463 | | |
464 | | class RewritesApplicator : public TransformActions::RewriteReceiver { |
465 | | Rewriter &rewriter; |
466 | | MigrationProcess::RewriteListener *Listener; |
467 | | |
468 | | public: |
469 | | RewritesApplicator(Rewriter &rewriter, ASTContext &ctx, |
470 | | MigrationProcess::RewriteListener *listener) |
471 | 72 | : rewriter(rewriter), Listener(listener) { |
472 | 72 | if (Listener) |
473 | 0 | Listener->start(ctx); |
474 | 72 | } |
475 | 72 | ~RewritesApplicator() override { |
476 | 72 | if (Listener) |
477 | 0 | Listener->finish(); |
478 | 72 | } |
479 | | |
480 | 313 | void insert(SourceLocation loc, StringRef text) override { |
481 | 313 | bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true, |
482 | 313 | /*indentNewLines=*/true); |
483 | 313 | if (!err && 313 Listener313 ) |
484 | 0 | Listener->insert(loc, text); |
485 | 313 | } |
486 | | |
487 | 402 | void remove(CharSourceRange range) override { |
488 | 402 | Rewriter::RewriteOptions removeOpts; |
489 | 402 | removeOpts.IncludeInsertsAtBeginOfRange = false; |
490 | 402 | removeOpts.IncludeInsertsAtEndOfRange = false; |
491 | 402 | removeOpts.RemoveLineIfEmpty = true; |
492 | 402 | |
493 | 402 | bool err = rewriter.RemoveText(range, removeOpts); |
494 | 402 | if (!err && 402 Listener402 ) |
495 | 0 | Listener->remove(range); |
496 | 402 | } |
497 | | |
498 | | void increaseIndentation(CharSourceRange range, |
499 | 16 | SourceLocation parentIndent) override { |
500 | 16 | rewriter.IncreaseIndentation(range, parentIndent); |
501 | 16 | } |
502 | | }; |
503 | | |
504 | | } // end anonymous namespace. |
505 | | |
506 | | /// \brief Anchor for VTable. |
507 | 0 | MigrationProcess::RewriteListener::~RewriteListener() { } |
508 | | |
509 | | MigrationProcess::MigrationProcess( |
510 | | const CompilerInvocation &CI, |
511 | | std::shared_ptr<PCHContainerOperations> PCHContainerOps, |
512 | | DiagnosticConsumer *diagClient, StringRef outputDir) |
513 | | : OrigCI(CI), PCHContainerOps(std::move(PCHContainerOps)), |
514 | 35 | DiagClient(diagClient), HadARCErrors(false) { |
515 | 35 | if (!outputDir.empty()35 ) { |
516 | 8 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
517 | 8 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
518 | 8 | new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(), |
519 | 8 | DiagClient, /*ShouldOwnClient=*/false)); |
520 | 8 | Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true); |
521 | 8 | } |
522 | 35 | } |
523 | | |
524 | | bool MigrationProcess::applyTransform(TransformFn trans, |
525 | 72 | RewriteListener *listener) { |
526 | 72 | std::unique_ptr<CompilerInvocation> CInvok; |
527 | 72 | CInvok.reset( |
528 | 72 | createInvocationForMigration(OrigCI, PCHContainerOps->getRawReader())); |
529 | 72 | CInvok->getDiagnosticOpts().IgnoreWarnings = true; |
530 | 72 | |
531 | 72 | Remapper.applyMappings(CInvok->getPreprocessorOpts()); |
532 | 72 | |
533 | 72 | CapturedDiagList capturedDiags; |
534 | 72 | std::vector<SourceLocation> ARCMTMacroLocs; |
535 | 72 | |
536 | 72 | assert(DiagClient); |
537 | 72 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
538 | 72 | IntrusiveRefCntPtr<DiagnosticsEngine> Diags( |
539 | 72 | new DiagnosticsEngine(DiagID, new DiagnosticOptions, |
540 | 72 | DiagClient, /*ShouldOwnClient=*/false)); |
541 | 72 | |
542 | 72 | // Filter of all diagnostics. |
543 | 72 | CaptureDiagnosticConsumer errRec(*Diags, *DiagClient, capturedDiags); |
544 | 72 | Diags->setClient(&errRec, /*ShouldOwnClient=*/false); |
545 | 72 | |
546 | 72 | std::unique_ptr<ARCMTMacroTrackerAction> ASTAction; |
547 | 72 | ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs)); |
548 | 72 | |
549 | 72 | std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction( |
550 | 72 | std::move(CInvok), PCHContainerOps, Diags, ASTAction.get())); |
551 | 72 | if (!Unit72 ) { |
552 | 0 | errRec.FinishCapture(); |
553 | 0 | return true; |
554 | 0 | } |
555 | 72 | Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that. |
556 | 72 | |
557 | 38 | HadARCErrors = HadARCErrors || capturedDiags.hasErrors(); |
558 | 72 | |
559 | 72 | // Don't filter diagnostics anymore. |
560 | 72 | Diags->setClient(DiagClient, /*ShouldOwnClient=*/false); |
561 | 72 | |
562 | 72 | ASTContext &Ctx = Unit->getASTContext(); |
563 | 72 | |
564 | 72 | if (Diags->hasFatalErrorOccurred()72 ) { |
565 | 0 | Diags->Reset(); |
566 | 0 | DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); |
567 | 0 | capturedDiags.reportDiagnostics(*Diags); |
568 | 0 | DiagClient->EndSourceFile(); |
569 | 0 | errRec.FinishCapture(); |
570 | 0 | return true; |
571 | 0 | } |
572 | 72 | |
573 | 72 | // After parsing of source files ended, we want to reuse the |
574 | 72 | // diagnostics objects to emit further diagnostics. |
575 | 72 | // We call BeginSourceFile because DiagnosticConsumer requires that |
576 | 72 | // diagnostics with source range information are emitted only in between |
577 | 72 | // BeginSourceFile() and EndSourceFile(). |
578 | 72 | DiagClient->BeginSourceFile(Ctx.getLangOpts(), &Unit->getPreprocessor()); |
579 | 72 | |
580 | 72 | Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); |
581 | 72 | TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor()); |
582 | 72 | MigrationPass pass(Ctx, OrigCI.getLangOpts()->getGC(), |
583 | 72 | Unit->getSema(), TA, capturedDiags, ARCMTMacroLocs); |
584 | 72 | |
585 | 72 | trans(pass); |
586 | 72 | |
587 | 72 | { |
588 | 72 | RewritesApplicator applicator(rewriter, Ctx, listener); |
589 | 72 | TA.applyRewrites(applicator); |
590 | 72 | } |
591 | 72 | |
592 | 72 | DiagClient->EndSourceFile(); |
593 | 72 | errRec.FinishCapture(); |
594 | 72 | |
595 | 72 | if (DiagClient->getNumErrors()) |
596 | 0 | return true; |
597 | 72 | |
598 | 72 | for (Rewriter::buffer_iterator |
599 | 149 | I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E149 ; ++I77 ) { |
600 | 77 | FileID FID = I->first; |
601 | 77 | RewriteBuffer &buf = I->second; |
602 | 77 | const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID); |
603 | 77 | assert(file); |
604 | 77 | std::string newFname = file->getName(); |
605 | 77 | newFname += "-trans"; |
606 | 77 | SmallString<512> newText; |
607 | 77 | llvm::raw_svector_ostream vecOS(newText); |
608 | 77 | buf.write(vecOS); |
609 | 77 | std::unique_ptr<llvm::MemoryBuffer> memBuf( |
610 | 77 | llvm::MemoryBuffer::getMemBufferCopy( |
611 | 77 | StringRef(newText.data(), newText.size()), newFname)); |
612 | 77 | SmallString<64> filePath(file->getName()); |
613 | 77 | Unit->getFileManager().FixupRelativePath(filePath); |
614 | 77 | Remapper.remap(filePath.str(), std::move(memBuf)); |
615 | 77 | } |
616 | 72 | |
617 | 72 | return false; |
618 | 72 | } |