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